Merge branch 'release/2024-03-01' into rome/release/2024-03-01
This commit is contained in:
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,6 +3,15 @@
|
|||||||
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
## High level Schema Overview
|
## High level Schema Overview
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
@@ -36,6 +45,38 @@ const schema = {
|
|||||||
Filters effect the where clause of the graphQL query. They are used to filter the data returned from the server.
|
Filters effect the where clause of the graphQL query. They are used to filter the data returned from the server.
|
||||||
A note on special notation used in the `name` field.
|
A note on special notation used in the `name` field.
|
||||||
|
|
||||||
|
## Reflection
|
||||||
|
|
||||||
|
Filters can make use of reflection to pre-fill select boxes, the following is an example of that in the filters file.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "jobs.status",
|
||||||
|
"translation": "jobs.fields.status",
|
||||||
|
"label": "Status",
|
||||||
|
"type": "string",
|
||||||
|
"reflector": {
|
||||||
|
"type": "internal",
|
||||||
|
"name": "special.job_statuses"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
in this example, a reflector with the type 'internal' (all types at the moment require this, and it is used for future functionality), with a name of `special.job_statuses`
|
||||||
|
|
||||||
|
The following cases are available
|
||||||
|
|
||||||
|
- `special.job_statuses` - This will reflect the statuses of the jobs table `bodyshop.md_ro_statuses.statuses'`
|
||||||
|
- `special.cost_centers` - This will reflect the cost centers `bodyshop.md_responsibility_centers.costs`
|
||||||
|
- `special.categories` - This will reflect the categories `bodyshop.md_categories`
|
||||||
|
- `special.insurance_companies` - This will reflect the insurance companies `bodyshop.md_ins_cos`'
|
||||||
|
- `special.employee_teams` - This will reflect the employee teams `bodyshop.employee_teams`
|
||||||
|
- `special.employees` - This will reflect the employees `bodyshop.employees`
|
||||||
|
- `special.first_names` - This will reflect the first names `bodyshop.employees`
|
||||||
|
- `special.last_names` - This will reflect the last names `bodyshop.employees`
|
||||||
|
- `special.referral_sources` - This will reflect the referral sources `bodyshop.md_referral_sources
|
||||||
|
- `special.class`- This will reflect the class `bodyshop.md_classes`
|
||||||
|
-
|
||||||
### Path without brackets, multi level
|
### Path without brackets, multi level
|
||||||
|
|
||||||
`"name": "jobs.joblines.mod_lb_hrs",`
|
`"name": "jobs.joblines.mod_lb_hrs",`
|
||||||
@@ -71,8 +112,8 @@ 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.
|
||||||
|
|
||||||
@@ -107,14 +148,37 @@ 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.
|
||||||
|
|
||||||
|
|
||||||
## 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
|
||||||
|
|
||||||
|
- A sorter can be given a default object containing a `order` and `direction` key value. This will be used to sort the report if the user does not select any of the sorters themselves.
|
||||||
|
- The `order` key is the order in which the sorters are applied, and the `direction` key is the direction of the sort, either `asc` or `desc`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "jobs.joblines.mod_lb_hrs",
|
||||||
|
"translation": "jobs.joblines.mod_lb_hrs_1",
|
||||||
|
"label": "mod_lb_hrs_1",
|
||||||
|
"type": "number",
|
||||||
|
"default": {
|
||||||
|
"order": 1,
|
||||||
|
"direction": "asc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -3,10 +3,22 @@ import { useMutation } from "@apollo/client";
|
|||||||
import { Button, notification, Popconfirm } from "antd";
|
import { Button, notification, Popconfirm } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
import { DELETE_BILL } from "../../graphql/bills.queries";
|
import { DELETE_BILL } from "../../graphql/bills.queries";
|
||||||
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
|
|
||||||
export default function BillDeleteButton({ bill, callback }) {
|
const mapStateToProps = createStructuredSelector({});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
insertAuditTrail: ({ jobid, operation }) =>
|
||||||
|
dispatch(insertAuditTrail({ jobid, operation })),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(BillDeleteButton);
|
||||||
|
|
||||||
|
export function BillDeleteButton({ bill, jobid, callback, insertAuditTrail }) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [deleteBill] = useMutation(DELETE_BILL);
|
const [deleteBill] = useMutation(DELETE_BILL);
|
||||||
@@ -36,6 +48,10 @@ export default function BillDeleteButton({ bill, callback }) {
|
|||||||
|
|
||||||
if (!!!result.errors) {
|
if (!!!result.errors) {
|
||||||
notification["success"]({ message: t("bills.successes.deleted") });
|
notification["success"]({ message: t("bills.successes.deleted") });
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: jobid,
|
||||||
|
operation: AuditTrailMapping.billdeleted(bill.invoice_number),
|
||||||
|
});
|
||||||
|
|
||||||
if (callback && typeof callback === "function") callback(bill.id);
|
if (callback && typeof callback === "function") callback(bill.id);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import { setModalContext } from "../../redux/modals/modals.actions";
|
|||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import BillDeleteButton from "../bill-delete-button/bill-delete-button.component";
|
import BillDeleteButton from "../bill-delete-button/bill-delete-button.component";
|
||||||
import BillDetailEditReturnComponent from "../bill-detail-edit/bill-detail-edit-return.component";
|
import BillDetailEditReturnComponent from "../bill-detail-edit/bill-detail-edit-return.component";
|
||||||
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
||||||
@@ -58,7 +58,7 @@ export function BillsListTableComponent({
|
|||||||
<EditFilled />
|
<EditFilled />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<BillDeleteButton bill={record} />
|
<BillDeleteButton bill={record} jobid={job.id} />
|
||||||
<BillDetailEditReturnComponent
|
<BillDetailEditReturnComponent
|
||||||
data={{ bills_by_pk: { ...record, jobid: job.id } }}
|
data={{ bills_by_pk: { ...record, jobid: job.id } }}
|
||||||
disabled={
|
disabled={
|
||||||
|
|||||||
@@ -68,6 +68,30 @@ export default function ContractFormComponent({
|
|||||||
<FormDateTimePicker />
|
<FormDateTimePicker />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
|
{create && (
|
||||||
|
<Form.Item
|
||||||
|
shouldUpdate={(p, c) => p.scheduledreturn !== c.scheduledreturn}
|
||||||
|
>
|
||||||
|
{() => {
|
||||||
|
const insuranceOver =
|
||||||
|
selectedCar &&
|
||||||
|
selectedCar.insuranceexpires &&
|
||||||
|
moment(selectedCar.insuranceexpires)
|
||||||
|
.endOf("day")
|
||||||
|
.isBefore(moment(form.getFieldValue("scheduledreturn")));
|
||||||
|
if (insuranceOver)
|
||||||
|
return (
|
||||||
|
<Space direction="vertical" style={{ color: "tomato" }}>
|
||||||
|
<span>
|
||||||
|
<WarningFilled style={{ marginRight: ".3rem" }} />
|
||||||
|
{t("contracts.labels.insuranceexpired")}
|
||||||
|
</span>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
return <></>;
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow grow>
|
<LayoutFormRow grow>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -90,16 +114,17 @@ export default function ContractFormComponent({
|
|||||||
>
|
>
|
||||||
{() => {
|
{() => {
|
||||||
const mileageOver =
|
const mileageOver =
|
||||||
selectedCar &&
|
selectedCar && selectedCar.nextservicekm
|
||||||
selectedCar.nextservicekm <= form.getFieldValue("kmstart");
|
? selectedCar.nextservicekm <= form.getFieldValue("kmstart")
|
||||||
|
: false;
|
||||||
const dueForService =
|
const dueForService =
|
||||||
selectedCar &&
|
selectedCar &&
|
||||||
selectedCar.nextservicedate &&
|
selectedCar.nextservicedate &&
|
||||||
moment(selectedCar.nextservicedate).isBefore(
|
moment(selectedCar.nextservicedate)
|
||||||
moment(form.getFieldValue("scheduledreturn"))
|
.endOf("day")
|
||||||
);
|
.isSameOrBefore(
|
||||||
|
moment(form.getFieldValue("scheduledreturn"))
|
||||||
|
);
|
||||||
if (mileageOver || dueForService)
|
if (mileageOver || dueForService)
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ color: "tomato" }}>
|
<Space direction="vertical" style={{ color: "tomato" }}>
|
||||||
@@ -117,7 +142,6 @@ export default function ContractFormComponent({
|
|||||||
</span>
|
</span>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@@ -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,6 +55,7 @@ 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"),
|
||||||
@@ -72,7 +78,8 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
|||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
const { nextservicedate, nextservicekm, mileage } = record;
|
const { nextservicedate, nextservicekm, mileage, insuranceexpires } =
|
||||||
|
record;
|
||||||
|
|
||||||
const mileageOver = nextservicekm ? nextservicekm <= mileage : false;
|
const mileageOver = nextservicekm ? nextservicekm <= mileage : false;
|
||||||
|
|
||||||
@@ -80,11 +87,25 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
|||||||
nextservicedate &&
|
nextservicedate &&
|
||||||
moment(nextservicedate).endOf("day").isSameOrBefore(moment());
|
moment(nextservicedate).endOf("day").isSameOrBefore(moment());
|
||||||
|
|
||||||
|
const insuranceOver =
|
||||||
|
insuranceexpires &&
|
||||||
|
moment(insuranceexpires).endOf("day").isBefore(moment());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space>
|
<Space>
|
||||||
{t(record.status)}
|
{t(record.status)}
|
||||||
{(mileageOver || dueForService) && (
|
{(mileageOver || dueForService || insuranceOver) && (
|
||||||
<Tooltip title={t("contracts.labels.cardueforservice")}>
|
<Tooltip
|
||||||
|
title={
|
||||||
|
(mileageOver || dueForService) && insuranceOver
|
||||||
|
? t("contracts.labels.insuranceexpired") +
|
||||||
|
" / " +
|
||||||
|
t("contracts.labels.cardueforservice")
|
||||||
|
: insuranceOver
|
||||||
|
? t("contracts.labels.insuranceexpired")
|
||||||
|
: t("contracts.labels.cardueforservice")
|
||||||
|
}
|
||||||
|
>
|
||||||
<WarningFilled style={{ color: "tomato" }} />
|
<WarningFilled style={{ color: "tomato" }} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
@@ -97,6 +118,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"),
|
||||||
@@ -212,7 +234,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
|
||||||
|
|||||||
@@ -3,21 +3,32 @@ import {
|
|||||||
ExclamationCircleFilled,
|
ExclamationCircleFilled,
|
||||||
PauseCircleOutlined,
|
PauseCircleOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { Card, Space, Table, Tooltip } from "antd";
|
import { Card, Space, Switch, Table, Tooltip, Typography } from "antd";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { TimeFormatter } from "../../../utils/DateFormatter";
|
||||||
|
import { onlyUnique } from "../../../utils/arrayHelper";
|
||||||
|
import { alphaSort, dateSort } from "../../../utils/sorters";
|
||||||
|
import useLocalStorage from "../../../utils/useLocalStorage";
|
||||||
import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
|
import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
|
||||||
import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay, {
|
||||||
|
OwnerNameDisplayFunction,
|
||||||
|
} from "../../owner-name-display/owner-name-display.component";
|
||||||
import DashboardRefreshRequired from "../refresh-required.component";
|
import DashboardRefreshRequired from "../refresh-required.component";
|
||||||
import {pageLimit} from "../../../utils/config";
|
|
||||||
|
|
||||||
export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
|
filteredInfo: {},
|
||||||
});
|
});
|
||||||
|
const [isTvModeScheduledIn, setIsTvModeScheduledIn] = useLocalStorage(
|
||||||
|
"isTvModeScheduledIn",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
if (!data) return null;
|
if (!data) return null;
|
||||||
if (!data.scheduled_in_today)
|
if (!data.scheduled_in_today)
|
||||||
return <DashboardRefreshRequired {...cardProps} />;
|
return <DashboardRefreshRequired {...cardProps} />;
|
||||||
@@ -31,6 +42,12 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
alt_transport: item.job.alt_transport,
|
alt_transport: item.job.alt_transport,
|
||||||
clm_no: item.job.clm_no,
|
clm_no: item.job.clm_no,
|
||||||
jobid: item.job.jobid,
|
jobid: item.job.jobid,
|
||||||
|
joblines_body: item.job.joblines
|
||||||
|
.filter((l) => l.mod_lbr_ty !== "LAR")
|
||||||
|
.reduce((acc, val) => acc + val.mod_lb_hrs, 0),
|
||||||
|
joblines_ref: item.job.joblines
|
||||||
|
.filter((l) => l.mod_lbr_ty === "LAR")
|
||||||
|
.reduce((acc, val) => acc + val.mod_lb_hrs, 0),
|
||||||
ins_co_nm: item.job.ins_co_nm,
|
ins_co_nm: item.job.ins_co_nm,
|
||||||
iouparent: item.job.iouparent,
|
iouparent: item.job.iouparent,
|
||||||
ownerid: item.job.ownerid,
|
ownerid: item.job.ownerid,
|
||||||
@@ -49,7 +66,7 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
v_vin: item.job.v_vin,
|
v_vin: item.job.v_vin,
|
||||||
vehicleid: item.job.vehicleid,
|
vehicleid: item.job.vehicleid,
|
||||||
note: item.note,
|
note: item.note,
|
||||||
start: moment(item.start).format("hh:mm a"),
|
start: item.start,
|
||||||
title: item.title,
|
title: item.title,
|
||||||
};
|
};
|
||||||
appt.push(i);
|
appt.push(i);
|
||||||
@@ -59,11 +76,192 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
return new moment(a.start) - new moment(b.start);
|
return new moment(a.start) - new moment(b.start);
|
||||||
});
|
});
|
||||||
|
|
||||||
const columns = [
|
const tvFontSize = 16;
|
||||||
|
const tvFontWeight = "bold";
|
||||||
|
|
||||||
|
const tvColumns = [
|
||||||
|
{
|
||||||
|
title: t("appointments.fields.time"),
|
||||||
|
dataIndex: "start",
|
||||||
|
key: "start",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) => dateSort(a.start, b.start),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "start" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
<TimeFormatter>{record.start}</TimeFormatter>
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ro_number"),
|
title: t("jobs.fields.ro_number"),
|
||||||
dataIndex: "ro_number",
|
dataIndex: "ro_number",
|
||||||
key: "ro_number",
|
key: "ro_number",
|
||||||
|
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => (
|
||||||
|
<Link
|
||||||
|
to={"/manage/jobs/" + record.jobid}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<Space>
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.ro_number || t("general.labels.na")}
|
||||||
|
{record.production_vars && record.production_vars.alert ? (
|
||||||
|
<ExclamationCircleFilled className="production-alert" />
|
||||||
|
) : null}
|
||||||
|
{record.suspended && (
|
||||||
|
<PauseCircleOutlined style={{ color: "orangered" }} />
|
||||||
|
)}
|
||||||
|
{record.iouparent && (
|
||||||
|
<Tooltip title={t("jobs.labels.iou")}>
|
||||||
|
<BranchesOutlined style={{ color: "orangered" }} />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</Space>
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.owner"),
|
||||||
|
dataIndex: "owner",
|
||||||
|
key: "owner",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => {
|
||||||
|
return record.ownerid ? (
|
||||||
|
<Link
|
||||||
|
to={"/manage/owners/" + record.ownerid}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.vehicle"),
|
||||||
|
dataIndex: "vehicle",
|
||||||
|
key: "vehicle",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
`${a.v_model_yr || ""} ${a.v_make_desc || ""} ${
|
||||||
|
a.v_model_desc || ""
|
||||||
|
}`,
|
||||||
|
`${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}`
|
||||||
|
),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => {
|
||||||
|
return record.vehicleid ? (
|
||||||
|
<Link
|
||||||
|
to={"/manage/vehicles/" + record.vehicleid}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||||
|
record.v_model_desc || ""
|
||||||
|
}`}
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>{`${
|
||||||
|
record.v_model_yr || ""
|
||||||
|
} ${record.v_make_desc || ""} ${record.v_model_desc || ""}`}</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("appointments.fields.alt_transport"),
|
||||||
|
dataIndex: "alt_transport",
|
||||||
|
key: "alt_transport",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "alt_transport" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
filters:
|
||||||
|
(appt &&
|
||||||
|
appt
|
||||||
|
.map((j) => j.alt_transport)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Alt. Transport",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.alt_transport),
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.alt_transport}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.lab"),
|
||||||
|
dataIndex: "joblines_body",
|
||||||
|
key: "joblines_body",
|
||||||
|
sorter: (a, b) => a.joblines_body - b.joblines_body,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "joblines_body" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
align: "right",
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.joblines_body.toFixed(1)}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.lar"),
|
||||||
|
dataIndex: "joblines_ref",
|
||||||
|
key: "joblines_ref",
|
||||||
|
sorter: (a, b) => a.joblines_ref - b.joblines_ref,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "joblines_ref" && state.sortedInfo.order,
|
||||||
|
align: "right",
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.joblines_ref.toFixed(1)}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("appointments.fields.time"),
|
||||||
|
dataIndex: "start",
|
||||||
|
key: "start",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) => dateSort(a.start, b.start),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "start" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => <TimeFormatter>{record.start}</TimeFormatter>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.ro_number"),
|
||||||
|
dataIndex: "ro_number",
|
||||||
|
key: "ro_number",
|
||||||
|
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Link
|
<Link
|
||||||
to={"/manage/jobs/" + record.jobid}
|
to={"/manage/jobs/" + record.jobid}
|
||||||
@@ -91,7 +289,10 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
dataIndex: "owner",
|
dataIndex: "owner",
|
||||||
key: "owner",
|
key: "owner",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
sorter: (a, b) =>
|
||||||
|
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.ownerid ? (
|
return record.ownerid ? (
|
||||||
<Link
|
<Link
|
||||||
@@ -108,23 +309,16 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ownr_ph1"),
|
title: t("dashboard.labels.phone"),
|
||||||
dataIndex: "ownr_ph1",
|
dataIndex: "ownr_ph",
|
||||||
key: "ownr_ph1",
|
key: "ownr_ph",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<ChatOpenButton phone={record.ownr_ph1} jobid={record.jobid} />
|
<Space size="small" wrap>
|
||||||
),
|
<ChatOpenButton phone={record.ownr_ph1} jobid={record.jobid} />
|
||||||
},
|
<ChatOpenButton phone={record.ownr_ph2} jobid={record.jobid} />
|
||||||
{
|
</Space>
|
||||||
title: t("jobs.fields.ownr_ph2"),
|
|
||||||
dataIndex: "ownr_ph2",
|
|
||||||
key: "ownr_ph2",
|
|
||||||
ellipsis: true,
|
|
||||||
responsive: ["md"],
|
|
||||||
render: (text, record) => (
|
|
||||||
<ChatOpenButton phone={record.ownr_ph2} jobid={record.jobid} />
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -134,7 +328,7 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<ChatOpenButton phone={record.ownr_ea} jobid={record.jobid} />
|
<a href={`mailto:${record.ownr_ea}`}>{record.ownr_ea}</a>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -142,6 +336,15 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
dataIndex: "vehicle",
|
dataIndex: "vehicle",
|
||||||
key: "vehicle",
|
key: "vehicle",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
`${a.v_model_yr || ""} ${a.v_make_desc || ""} ${
|
||||||
|
a.v_model_desc || ""
|
||||||
|
}`,
|
||||||
|
`${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}`
|
||||||
|
),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.vehicleid ? (
|
return record.vehicleid ? (
|
||||||
<Link
|
<Link
|
||||||
@@ -165,43 +368,80 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
|||||||
key: "ins_co_nm",
|
key: "ins_co_nm",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
},
|
sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm),
|
||||||
{
|
sortOrder:
|
||||||
title: t("appointments.fields.time"),
|
state.sortedInfo.columnKey === "ins_co_nm" && state.sortedInfo.order,
|
||||||
dataIndex: "start",
|
filters:
|
||||||
key: "start",
|
(appt &&
|
||||||
ellipsis: true,
|
appt
|
||||||
responsive: ["md"],
|
.map((j) => j.ins_co_nm)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Ins. Co.*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("appointments.fields.alt_transport"),
|
title: t("appointments.fields.alt_transport"),
|
||||||
dataIndex: "alt_transport",
|
dataIndex: "alt_transport",
|
||||||
key: "alt_transport",
|
key: "alt_transport",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "alt_transport" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
filters:
|
||||||
|
(appt &&
|
||||||
|
appt
|
||||||
|
.map((j) => j.alt_transport)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Alt. Transport",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.alt_transport),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleTableChange = (sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
setState({ ...state, sortedInfo: sorter });
|
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
title={t("dashboard.titles.scheduledintoday", {
|
title={t("dashboard.titles.scheduledindate", {
|
||||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
date: moment().startOf("day").format("MM/DD/YYYY"),
|
||||||
})}
|
})}
|
||||||
|
extra={
|
||||||
|
<Space>
|
||||||
|
<Typography.Text>{t("general.labels.tvmode")}</Typography.Text>
|
||||||
|
<Switch
|
||||||
|
onClick={() => setIsTvModeScheduledIn(!isTvModeScheduledIn)}
|
||||||
|
defaultChecked={isTvModeScheduledIn}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
{...cardProps}
|
{...cardProps}
|
||||||
>
|
>
|
||||||
<div style={{ height: "100%" }}>
|
<div style={{ height: "100%" }}>
|
||||||
<Table
|
<Table
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
pagination={{ position: "top", defaultPageSize: pageLimit }}
|
pagination={false}
|
||||||
columns={columns}
|
columns={isTvModeScheduledIn ? tvColumns : columns}
|
||||||
scroll={{ x: true, y: "calc(100% - 2em)" }}
|
scroll={{ x: true, y: "calc(100% - 2em)" }}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
style={{ height: "85%" }}
|
style={{ height: "85%" }}
|
||||||
dataSource={appt}
|
dataSource={appt}
|
||||||
|
size={isTvModeScheduledIn ? "small" : "middle"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -220,6 +460,10 @@ export const DashboardScheduledInTodayGql = `
|
|||||||
alt_transport
|
alt_transport
|
||||||
clm_no
|
clm_no
|
||||||
jobid: id
|
jobid: id
|
||||||
|
joblines(where: {removed: {_eq: false}}) {
|
||||||
|
mod_lb_hrs
|
||||||
|
mod_lbr_ty
|
||||||
|
}
|
||||||
ins_co_nm
|
ins_co_nm
|
||||||
iouparent
|
iouparent
|
||||||
ownerid
|
ownerid
|
||||||
|
|||||||
@@ -3,37 +3,272 @@ import {
|
|||||||
ExclamationCircleFilled,
|
ExclamationCircleFilled,
|
||||||
PauseCircleOutlined,
|
PauseCircleOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { Card, Space, Table, Tooltip } from "antd";
|
import { Card, Space, Switch, Table, Tooltip, Typography } from "antd";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { TimeFormatter } from "../../../utils/DateFormatter";
|
||||||
|
import { onlyUnique } from "../../../utils/arrayHelper";
|
||||||
|
import { alphaSort, dateSort } from "../../../utils/sorters";
|
||||||
|
import useLocalStorage from "../../../utils/useLocalStorage";
|
||||||
import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
|
import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
|
||||||
import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay, {
|
||||||
|
OwnerNameDisplayFunction,
|
||||||
|
} from "../../owner-name-display/owner-name-display.component";
|
||||||
import DashboardRefreshRequired from "../refresh-required.component";
|
import DashboardRefreshRequired from "../refresh-required.component";
|
||||||
import {pageLimit} from "../../../utils/config";
|
|
||||||
|
|
||||||
export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
|
filteredInfo: {},
|
||||||
});
|
});
|
||||||
|
const [isTvModeScheduledOut, setIsTvModeScheduledOut] = useLocalStorage(
|
||||||
|
"isTvModeScheduledOut",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
if (!data) return null;
|
if (!data) return null;
|
||||||
if (!data.scheduled_out_today)
|
if (!data.scheduled_out_today)
|
||||||
return <DashboardRefreshRequired {...cardProps} />;
|
return <DashboardRefreshRequired {...cardProps} />;
|
||||||
|
|
||||||
data.scheduled_out_today.forEach((item) => {
|
data.scheduled_out_today.forEach((item) => {
|
||||||
item.scheduled_completion= moment(item.scheduled_completion).format("hh:mm a")
|
item.joblines_body = item.joblines
|
||||||
|
? item.joblines
|
||||||
|
.filter((l) => l.mod_lbr_ty !== "LAR")
|
||||||
|
.reduce((acc, val) => acc + val.mod_lb_hrs, 0)
|
||||||
|
: 0;
|
||||||
|
item.joblines_ref = item.joblines
|
||||||
|
? item.joblines
|
||||||
|
.filter((l) => l.mod_lbr_ty === "LAR")
|
||||||
|
.reduce((acc, val) => acc + val.mod_lb_hrs, 0)
|
||||||
|
: 0;
|
||||||
});
|
});
|
||||||
data.scheduled_out_today.sort(function (a, b) {
|
data.scheduled_out_today.sort(function (a, b) {
|
||||||
return new Date(a.scheduled_completion) - new Date(b.scheduled_completion);
|
return new Date(a.scheduled_completion) - new Date(b.scheduled_completion);
|
||||||
});
|
});
|
||||||
|
|
||||||
const columns = [
|
const tvFontSize = 18;
|
||||||
|
const tvFontWeight = "bold";
|
||||||
|
|
||||||
|
const tvColumns = [
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.scheduled_completion"),
|
||||||
|
dataIndex: "scheduled_completion",
|
||||||
|
key: "scheduled_completion",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
dateSort(a.scheduled_completion, b.scheduled_completion),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "scheduled_completion" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
<TimeFormatter>{record.scheduled_completion}</TimeFormatter>
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ro_number"),
|
title: t("jobs.fields.ro_number"),
|
||||||
dataIndex: "ro_number",
|
dataIndex: "ro_number",
|
||||||
key: "ro_number",
|
key: "ro_number",
|
||||||
|
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => (
|
||||||
|
<Link
|
||||||
|
to={"/manage/jobs/" + record.jobid}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<Space>
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.ro_number || t("general.labels.na")}
|
||||||
|
{record.production_vars && record.production_vars.alert ? (
|
||||||
|
<ExclamationCircleFilled className="production-alert" />
|
||||||
|
) : null}
|
||||||
|
{record.suspended && (
|
||||||
|
<PauseCircleOutlined style={{ color: "orangered" }} />
|
||||||
|
)}
|
||||||
|
{record.iouparent && (
|
||||||
|
<Tooltip title={t("jobs.labels.iou")}>
|
||||||
|
<BranchesOutlined style={{ color: "orangered" }} />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</Space>
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.owner"),
|
||||||
|
dataIndex: "owner",
|
||||||
|
key: "owner",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => {
|
||||||
|
return record.ownerid ? (
|
||||||
|
<Link
|
||||||
|
to={"/manage/owners/" + record.ownerid}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
<OwnerNameDisplay ownerObject={record} />
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.vehicle"),
|
||||||
|
dataIndex: "vehicle",
|
||||||
|
key: "vehicle",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
`${a.v_model_yr || ""} ${a.v_make_desc || ""} ${
|
||||||
|
a.v_model_desc || ""
|
||||||
|
}`,
|
||||||
|
`${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}`
|
||||||
|
),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => {
|
||||||
|
return record.vehicleid ? (
|
||||||
|
<Link
|
||||||
|
to={"/manage/vehicles/" + record.vehicleid}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||||
|
record.v_model_desc || ""
|
||||||
|
}`}
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>{`${
|
||||||
|
record.v_model_yr || ""
|
||||||
|
} ${record.v_make_desc || ""} ${record.v_model_desc || ""}`}</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("appointments.fields.alt_transport"),
|
||||||
|
dataIndex: "alt_transport",
|
||||||
|
key: "alt_transport",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "alt_transport" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
filters:
|
||||||
|
(data.scheduled_out_today &&
|
||||||
|
data.scheduled_out_today
|
||||||
|
.map((j) => j.alt_transport)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Alt. Transport*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.alt_transport),
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.alt_transport}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.status"),
|
||||||
|
dataIndex: "status",
|
||||||
|
key: "status",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
|
filters:
|
||||||
|
(data.scheduled_out_today &&
|
||||||
|
data.scheduled_out_today
|
||||||
|
.map((j) => j.status)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Status*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.status),
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.status}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.lab"),
|
||||||
|
dataIndex: "joblines_body",
|
||||||
|
key: "joblines_body",
|
||||||
|
sorter: (a, b) => a.joblines_body - b.joblines_body,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "joblines_body" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
align: "right",
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.joblines_body.toFixed(1)}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.lar"),
|
||||||
|
dataIndex: "joblines_ref",
|
||||||
|
key: "joblines_ref",
|
||||||
|
sorter: (a, b) => a.joblines_ref - b.joblines_ref,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "joblines_ref" && state.sortedInfo.order,
|
||||||
|
align: "right",
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ fontSize: tvFontSize, fontWeight: tvFontWeight }}>
|
||||||
|
{record.joblines_ref.toFixed(1)}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.scheduled_completion"),
|
||||||
|
dataIndex: "scheduled_completion",
|
||||||
|
key: "scheduled_completion",
|
||||||
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
dateSort(a.scheduled_completion, b.scheduled_completion),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "scheduled_completion" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
render: (text, record) => (
|
||||||
|
<TimeFormatter>{record.scheduled_completion}</TimeFormatter>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.ro_number"),
|
||||||
|
dataIndex: "ro_number",
|
||||||
|
key: "ro_number",
|
||||||
|
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Link
|
<Link
|
||||||
to={"/manage/jobs/" + record.jobid}
|
to={"/manage/jobs/" + record.jobid}
|
||||||
@@ -61,7 +296,10 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
|||||||
dataIndex: "owner",
|
dataIndex: "owner",
|
||||||
key: "owner",
|
key: "owner",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
sorter: (a, b) =>
|
||||||
|
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.ownerid ? (
|
return record.ownerid ? (
|
||||||
<Link
|
<Link
|
||||||
@@ -78,23 +316,16 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ownr_ph1"),
|
title: t("dashboard.labels.phone"),
|
||||||
dataIndex: "ownr_ph1",
|
dataIndex: "ownr_ph",
|
||||||
key: "ownr_ph1",
|
key: "ownr_ph",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<ChatOpenButton phone={record.ownr_ph1} jobid={record.jobid} />
|
<Space size="small" wrap>
|
||||||
),
|
<ChatOpenButton phone={record.ownr_ph1} jobid={record.jobid} />
|
||||||
},
|
<ChatOpenButton phone={record.ownr_ph2} jobid={record.jobid} />
|
||||||
{
|
</Space>
|
||||||
title: t("jobs.fields.ownr_ph2"),
|
|
||||||
dataIndex: "ownr_ph2",
|
|
||||||
key: "ownr_ph2",
|
|
||||||
ellipsis: true,
|
|
||||||
responsive: ["md"],
|
|
||||||
render: (text, record) => (
|
|
||||||
<ChatOpenButton phone={record.ownr_ph2} jobid={record.jobid} />
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -104,7 +335,7 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
|||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<ChatOpenButton phone={record.ownr_ea} jobid={record.jobid} />
|
<a href={`mailto:${record.ownr_ea}`}>{record.ownr_ea}</a>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -112,6 +343,15 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
|||||||
dataIndex: "vehicle",
|
dataIndex: "vehicle",
|
||||||
key: "vehicle",
|
key: "vehicle",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
`${a.v_model_yr || ""} ${a.v_make_desc || ""} ${
|
||||||
|
a.v_model_desc || ""
|
||||||
|
}`,
|
||||||
|
`${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}`
|
||||||
|
),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.vehicleid ? (
|
return record.vehicleid ? (
|
||||||
<Link
|
<Link
|
||||||
@@ -135,43 +375,80 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
|||||||
key: "ins_co_nm",
|
key: "ins_co_nm",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
},
|
sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm),
|
||||||
{
|
sortOrder:
|
||||||
title: t("jobs.fields.scheduled_completion"),
|
state.sortedInfo.columnKey === "ins_co_nm" && state.sortedInfo.order,
|
||||||
dataIndex: "scheduled_completion",
|
filters:
|
||||||
key: "scheduled_completion",
|
(data.scheduled_out_today &&
|
||||||
ellipsis: true,
|
data.scheduled_out_today
|
||||||
responsive: ["md"],
|
.map((j) => j.ins_co_nm)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Ins. Co.*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("appointments.fields.alt_transport"),
|
title: t("appointments.fields.alt_transport"),
|
||||||
dataIndex: "alt_transport",
|
dataIndex: "alt_transport",
|
||||||
key: "alt_transport",
|
key: "alt_transport",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
responsive: ["md"],
|
sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "alt_transport" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
filters:
|
||||||
|
(data.scheduled_out_today &&
|
||||||
|
data.scheduled_out_today
|
||||||
|
.map((j) => j.alt_transport)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Alt. Transport*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => alphaSort(a.text, b.text))) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.alt_transport),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleTableChange = (sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
setState({ ...state, sortedInfo: sorter });
|
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
title={t("dashboard.titles.scheduledouttoday", {
|
title={t("dashboard.titles.scheduledoutdate", {
|
||||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
date: moment().startOf("day").format("MM/DD/YYYY"),
|
||||||
})}
|
})}
|
||||||
|
extra={
|
||||||
|
<Space>
|
||||||
|
<Typography.Text>{t("general.labels.tvmode")}</Typography.Text>
|
||||||
|
<Switch
|
||||||
|
onClick={() => setIsTvModeScheduledOut(!isTvModeScheduledOut)}
|
||||||
|
defaultChecked={isTvModeScheduledOut}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
{...cardProps}
|
{...cardProps}
|
||||||
>
|
>
|
||||||
<div style={{ height: "100%" }}>
|
<div style={{ height: "100%" }}>
|
||||||
<Table
|
<Table
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
pagination={{ position: "top", defaultPageSize: pageLimit }}
|
pagination={false}
|
||||||
columns={columns}
|
columns={isTvModeScheduledOut ? tvColumns : columns}
|
||||||
scroll={{ x: true, y: "calc(100% - 2em)" }}
|
scroll={{ x: true, y: "calc(100% - 2em)" }}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
style={{ height: "85%" }}
|
style={{ height: "85%" }}
|
||||||
dataSource={data.scheduled_out_today}
|
dataSource={data.scheduled_out_today}
|
||||||
|
size={isTvModeScheduledOut ? "small" : "middle"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -188,6 +465,10 @@ export const DashboardScheduledOutTodayGql = `
|
|||||||
alt_transport
|
alt_transport
|
||||||
clm_no
|
clm_no
|
||||||
jobid: id
|
jobid: id
|
||||||
|
joblines(where: {removed: {_eq: false}}) {
|
||||||
|
mod_lb_hrs
|
||||||
|
mod_lbr_ty
|
||||||
|
}
|
||||||
ins_co_nm
|
ins_co_nm
|
||||||
iouparent
|
iouparent
|
||||||
ownerid
|
ownerid
|
||||||
@@ -200,6 +481,7 @@ export const DashboardScheduledOutTodayGql = `
|
|||||||
production_vars
|
production_vars
|
||||||
ro_number
|
ro_number
|
||||||
scheduled_completion
|
scheduled_completion
|
||||||
|
status
|
||||||
suspended
|
suspended
|
||||||
v_make_desc
|
v_make_desc
|
||||||
v_model_desc
|
v_model_desc
|
||||||
|
|||||||
@@ -275,26 +275,22 @@ const componentList = {
|
|||||||
h: 2,
|
h: 2,
|
||||||
},
|
},
|
||||||
ScheduleInToday: {
|
ScheduleInToday: {
|
||||||
label: i18next.t("dashboard.titles.scheduledintoday", {
|
label: i18next.t("dashboard.titles.scheduledintoday"),
|
||||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
|
||||||
}),
|
|
||||||
component: DashboardScheduledInToday,
|
component: DashboardScheduledInToday,
|
||||||
gqlFragment: DashboardScheduledInTodayGql,
|
gqlFragment: DashboardScheduledInTodayGql,
|
||||||
minW: 10,
|
minW: 6,
|
||||||
minH: 2,
|
minH: 2,
|
||||||
w: 10,
|
w: 10,
|
||||||
h: 2,
|
h: 3,
|
||||||
},
|
},
|
||||||
ScheduleOutToday: {
|
ScheduleOutToday: {
|
||||||
label: i18next.t("dashboard.titles.scheduledouttoday", {
|
label: i18next.t("dashboard.titles.scheduledouttoday"),
|
||||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
|
||||||
}),
|
|
||||||
component: DashboardScheduledOutToday,
|
component: DashboardScheduledOutToday,
|
||||||
gqlFragment: DashboardScheduledOutTodayGql,
|
gqlFragment: DashboardScheduledOutTodayGql,
|
||||||
minW: 10,
|
minW: 6,
|
||||||
minH: 2,
|
minH: 2,
|
||||||
w: 10,
|
w: 10,
|
||||||
h: 2,
|
h: 3,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -306,8 +302,7 @@ const createDashboardQuery = (state) => {
|
|||||||
.map((item, index) => componentList[item.i].gqlFragment || "")
|
.map((item, index) => componentList[item.i].gqlFragment || "")
|
||||||
.join("");
|
.join("");
|
||||||
return gql`
|
return gql`
|
||||||
query QUERY_DASHBOARD_DETAILS {
|
query QUERY_DASHBOARD_DETAILS { ${componentBasedAdditions || ""}
|
||||||
${componentBasedAdditions || ""}
|
|
||||||
monthly_sales: jobs(where: {_and: [
|
monthly_sales: jobs(where: {_and: [
|
||||||
{ voided: {_eq: false}},
|
{ voided: {_eq: false}},
|
||||||
{date_invoiced: {_gte: "${moment()
|
{date_invoiced: {_gte: "${moment()
|
||||||
@@ -317,11 +312,11 @@ const createDashboardQuery = (state) => {
|
|||||||
.endOf("month")
|
.endOf("month")
|
||||||
.endOf("day")
|
.endOf("day")
|
||||||
.toISOString()}"}}]}) {
|
.toISOString()}"}}]}) {
|
||||||
id
|
id
|
||||||
ro_number
|
ro_number
|
||||||
date_invoiced
|
date_invoiced
|
||||||
job_totals
|
job_totals
|
||||||
rate_la1
|
rate_la1
|
||||||
rate_la2
|
rate_la2
|
||||||
rate_la3
|
rate_la3
|
||||||
rate_la4
|
rate_la4
|
||||||
@@ -344,43 +339,42 @@ const createDashboardQuery = (state) => {
|
|||||||
rate_mapa
|
rate_mapa
|
||||||
rate_mash
|
rate_mash
|
||||||
rate_matd
|
rate_matd
|
||||||
joblines(where: { removed: { _eq: false } }) {
|
joblines(where: { removed: { _eq: false } }) {
|
||||||
id
|
id
|
||||||
mod_lbr_ty
|
mod_lbr_ty
|
||||||
mod_lb_hrs
|
mod_lb_hrs
|
||||||
act_price
|
act_price
|
||||||
part_qty
|
part_qty
|
||||||
part_type
|
part_type
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
production_jobs: jobs(where: { inproduction: { _eq: true } }) {
|
||||||
production_jobs: jobs(where: { inproduction: { _eq: true } }) {
|
id
|
||||||
|
ro_number
|
||||||
|
ins_co_nm
|
||||||
|
job_totals
|
||||||
|
joblines(where: { removed: { _eq: false } }) {
|
||||||
id
|
id
|
||||||
ro_number
|
mod_lbr_ty
|
||||||
ins_co_nm
|
mod_lb_hrs
|
||||||
job_totals
|
act_price
|
||||||
joblines(where: { removed: { _eq: false } }) {
|
part_qty
|
||||||
id
|
part_type
|
||||||
mod_lbr_ty
|
}
|
||||||
mod_lb_hrs
|
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }) {
|
||||||
act_price
|
aggregate {
|
||||||
part_qty
|
sum {
|
||||||
part_type
|
mod_lb_hrs
|
||||||
}
|
|
||||||
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }) {
|
|
||||||
aggregate {
|
|
||||||
sum {
|
|
||||||
mod_lb_hrs
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } }) {
|
}
|
||||||
aggregate {
|
larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } }) {
|
||||||
sum {
|
aggregate {
|
||||||
mod_lb_hrs
|
sum {
|
||||||
}
|
mod_lb_hrs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
}`;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -128,7 +128,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
.ant-card-body {
|
.ant-card-body {
|
||||||
height: 80%;
|
height: calc(100% - 2rem);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
// // background-color: red;
|
// // background-color: red;
|
||||||
// height: 90%;
|
// height: 90%;
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ import { createStructuredSelector } from "reselect";
|
|||||||
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||||
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
import {
|
import {
|
||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectCurrentUser,
|
selectCurrentUser,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
import client from "../../utils/GraphQLClient";
|
import client from "../../utils/GraphQLClient";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -20,6 +22,11 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
insertAuditTrail: ({ jobid, operation }) =>
|
||||||
|
dispatch(insertAuditTrail({ jobid, operation })),
|
||||||
|
});
|
||||||
|
|
||||||
function updateJobCache(items) {
|
function updateJobCache(items) {
|
||||||
client.cache.modify({
|
client.cache.modify({
|
||||||
id: "ROOT_QUERY",
|
id: "ROOT_QUERY",
|
||||||
@@ -40,6 +47,7 @@ export function JobsCloseExportButton({
|
|||||||
disabled,
|
disabled,
|
||||||
setSelectedJobs,
|
setSelectedJobs,
|
||||||
refetch,
|
refetch,
|
||||||
|
insertAuditTrail,
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -181,6 +189,10 @@ export function JobsCloseExportButton({
|
|||||||
key: "jobsuccessexport",
|
key: "jobsuccessexport",
|
||||||
message: t("jobs.successes.exported"),
|
message: t("jobs.successes.exported"),
|
||||||
});
|
});
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: jobId,
|
||||||
|
operation: AuditTrailMapping.jobexported(),
|
||||||
|
});
|
||||||
updateJobCache(
|
updateJobCache(
|
||||||
jobUpdateResponse.data.update_jobs.returning.map((job) => job.id)
|
jobUpdateResponse.data.update_jobs.returning.map((job) => job.id)
|
||||||
);
|
);
|
||||||
@@ -192,12 +204,20 @@ export function JobsCloseExportButton({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
|
if (
|
||||||
|
bodyshop.accountingconfig &&
|
||||||
|
bodyshop.accountingconfig.qbo &&
|
||||||
|
successfulTransactions.length > 0
|
||||||
|
) {
|
||||||
notification.open({
|
notification.open({
|
||||||
type: "success",
|
type: "success",
|
||||||
key: "jobsuccessexport",
|
key: "jobsuccessexport",
|
||||||
message: t("jobs.successes.exported"),
|
message: t("jobs.successes.exported"),
|
||||||
});
|
});
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: jobId,
|
||||||
|
operation: AuditTrailMapping.jobexported(),
|
||||||
|
});
|
||||||
updateJobCache([
|
updateJobCache([
|
||||||
...new Set(
|
...new Set(
|
||||||
successfulTransactions.map(
|
successfulTransactions.map(
|
||||||
@@ -227,4 +247,7 @@ export function JobsCloseExportButton({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(JobsCloseExportButton);
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(JobsCloseExportButton);
|
||||||
|
|||||||
@@ -132,6 +132,12 @@ export function JobsDetailHeaderActions({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: job.id,
|
||||||
|
operation: AuditTrailMapping.jobsuspend(
|
||||||
|
!!job.suspended ? !job.suspended : true
|
||||||
|
),
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusmenu = (
|
const statusmenu = (
|
||||||
@@ -561,6 +567,10 @@ export function JobsDetailHeaderActions({
|
|||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("jobs.successes.voided"),
|
message: t("jobs.successes.voided"),
|
||||||
});
|
});
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: job.id,
|
||||||
|
operation: AuditTrailMapping.jobvoid(),
|
||||||
|
});
|
||||||
//go back to jobs list.
|
//go back to jobs list.
|
||||||
history.push(`/manage/`);
|
history.push(`/manage/`);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -123,11 +123,16 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
</DataLabel>
|
</DataLabel>
|
||||||
{job.cccontracts.length > 0 && (
|
{job.cccontracts.length > 0 && (
|
||||||
<DataLabel label={t("jobs.labels.contracts")}>
|
<DataLabel label={t("jobs.labels.contracts")}>
|
||||||
{job.cccontracts.map((c) => (
|
{job.cccontracts.map((c, index) => (
|
||||||
<Link
|
<Space wrap>
|
||||||
key={c.id}
|
<Link
|
||||||
to={`/manage/courtesycars/contracts/${c.id}`}
|
key={c.id}
|
||||||
>{`${c.agreementnumber} - ${c.courtesycar.fleetnumber} ${c.courtesycar.year} ${c.courtesycar.make} ${c.courtesycar.model}`}</Link>
|
to={`/manage/courtesycars/contracts/${c.id}`}
|
||||||
|
>
|
||||||
|
{`${c.agreementnumber} - ${c.courtesycar.fleetnumber} ${c.courtesycar.year} ${c.courtesycar.make} ${c.courtesycar.model}`}
|
||||||
|
{index !== job.cccontracts.length - 1 ? "," : null}
|
||||||
|
</Link>
|
||||||
|
</Space>
|
||||||
))}
|
))}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ import { createStructuredSelector } from "reselect";
|
|||||||
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||||
import { UPDATE_JOBS } from "../../graphql/jobs.queries";
|
import { UPDATE_JOBS } from "../../graphql/jobs.queries";
|
||||||
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
import {
|
import {
|
||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectCurrentUser,
|
selectCurrentUser,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
import client from "../../utils/GraphQLClient";
|
import client from "../../utils/GraphQLClient";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -20,6 +22,11 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
insertAuditTrail: ({ jobid, operation }) =>
|
||||||
|
dispatch(insertAuditTrail({ jobid, operation })),
|
||||||
|
});
|
||||||
|
|
||||||
function updateJobCache(items) {
|
function updateJobCache(items) {
|
||||||
client.cache.modify({
|
client.cache.modify({
|
||||||
id: "ROOT_QUERY",
|
id: "ROOT_QUERY",
|
||||||
@@ -41,6 +48,7 @@ export function JobsExportAllButton({
|
|||||||
loadingCallback,
|
loadingCallback,
|
||||||
completedCallback,
|
completedCallback,
|
||||||
refetch,
|
refetch,
|
||||||
|
insertAuditTrail,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [updateJob] = useMutation(UPDATE_JOBS);
|
const [updateJob] = useMutation(UPDATE_JOBS);
|
||||||
@@ -177,6 +185,12 @@ export function JobsExportAllButton({
|
|||||||
key: "jobsuccessexport",
|
key: "jobsuccessexport",
|
||||||
message: t("jobs.successes.exported"),
|
message: t("jobs.successes.exported"),
|
||||||
});
|
});
|
||||||
|
jobUpdateResponse.data.update_jobs.returning.forEach((job) => {
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: job.id,
|
||||||
|
operation: AuditTrailMapping.jobexported(),
|
||||||
|
});
|
||||||
|
});
|
||||||
updateJobCache(
|
updateJobCache(
|
||||||
jobUpdateResponse.data.update_jobs.returning.map(
|
jobUpdateResponse.data.update_jobs.returning.map(
|
||||||
(job) => job.id
|
(job) => job.id
|
||||||
@@ -190,13 +204,17 @@ export function JobsExportAllButton({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
|
if (
|
||||||
|
bodyshop.accountingconfig &&
|
||||||
|
bodyshop.accountingconfig.qbo &&
|
||||||
|
successfulTransactions.length > 0
|
||||||
|
) {
|
||||||
notification.open({
|
notification.open({
|
||||||
type: "success",
|
type: "success",
|
||||||
key: "jobsuccessexport",
|
key: "jobsuccessexport",
|
||||||
message: t("jobs.successes.exported"),
|
message: t("jobs.successes.exported"),
|
||||||
});
|
});
|
||||||
updateJobCache([
|
const successfulTransactionsSet = [
|
||||||
...new Set(
|
...new Set(
|
||||||
successfulTransactions.map(
|
successfulTransactions.map(
|
||||||
(st) =>
|
(st) =>
|
||||||
@@ -207,7 +225,14 @@ export function JobsExportAllButton({
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
]);
|
];
|
||||||
|
if (successfulTransactionsSet.length > 0) {
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: successfulTransactionsSet[0],
|
||||||
|
operation: AuditTrailMapping.jobexported(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
updateJobCache(successfulTransactionsSet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -225,4 +250,7 @@ export function JobsExportAllButton({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(JobsExportAllButton);
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(JobsExportAllButton);
|
||||||
|
|||||||
@@ -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,52 +1,414 @@
|
|||||||
import {Button, Card, Checkbox, Col, Form, Input, InputNumber, Row, Select} from "antd";
|
import {Button, Card, Checkbox, Col, Form, Input, InputNumber, Row, Select} from "antd";
|
||||||
import React, {useEffect, useState} from "react";
|
import React, {useCallback, useEffect, useMemo, useState} from "react";
|
||||||
import {fetchFilterData} from "../../utils/RenderTemplate";
|
import {fetchFilterData} from "../../utils/RenderTemplate";
|
||||||
import {DeleteFilled} from "@ant-design/icons";
|
import {DeleteFilled} from "@ant-design/icons";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {getOperatorsByType} 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 {FormDatePicker} from "../form-date-picker/form-date-picker.component.jsx";
|
||||||
|
|
||||||
export default function ReportCenterModalFiltersSortersComponent({form}) {
|
export default function ReportCenterModalFiltersSortersComponent({form, bodyshop}) {
|
||||||
return (
|
return (
|
||||||
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
|
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
|
||||||
{() => {
|
{() => {
|
||||||
const key = form.getFieldValue("key");
|
const key = form.getFieldValue("key");
|
||||||
return <RenderFilters form={form} templateId={key}/>;
|
return <RenderFilters form={form} templateId={key} bodyshop={bodyshop}/>;
|
||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function RenderFilters({templateId, form}) {
|
/**
|
||||||
|
* Filters Section
|
||||||
|
* @param filters
|
||||||
|
* @param form
|
||||||
|
* @param bodyshop
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function FiltersSection({filters, form, bodyshop}) {
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card type='inner' title={t('reportcenter.labels.advanced_filters_filters')} style={{marginTop: '10px'}}>
|
||||||
|
<Form.List name={["filters"]}>
|
||||||
|
{(fields, {add, remove}) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<Form.Item key={field.key}>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={10}>
|
||||||
|
<Form.Item
|
||||||
|
key={`${index}field`}
|
||||||
|
label={t('reportcenter.labels.advanced_filters_filter_field')}
|
||||||
|
name={[field.name, "field"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
getPopupContainer={trigger => trigger.parentNode}
|
||||||
|
onChange={() => {
|
||||||
|
// Clear related Fields
|
||||||
|
form.setFieldValue(['filters', field.name, 'value'], null);
|
||||||
|
form.setFieldValue(['filters', field.name, 'operator'], null);
|
||||||
|
}}
|
||||||
|
options={
|
||||||
|
filters.map((f) => {
|
||||||
|
return {
|
||||||
|
value: f.name,
|
||||||
|
label: f?.translation ? (t(f.translation) === f.translation ? f.label : t(f.translation)) : f.label,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={6}>
|
||||||
|
<Form.Item
|
||||||
|
dependencies={[['filters', field.name, "field"],['filters', field.name, "value"]]}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
() => {
|
||||||
|
const name = form.getFieldValue(['filters', field.name, "field"]);
|
||||||
|
const type = filters.find(f => f.name === name)?.type;
|
||||||
|
|
||||||
|
return <Form.Item
|
||||||
|
key={`${index}operator`}
|
||||||
|
label={t('reportcenter.labels.advanced_filters_filter_operator')}
|
||||||
|
name={[field.name, "operator"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
getPopupContainer={trigger => trigger.parentNode}
|
||||||
|
options={ getWhereOperatorsByType(type)}
|
||||||
|
onChange={() => {
|
||||||
|
// Clear related Fields
|
||||||
|
|
||||||
|
form.setFieldValue(['filters', field.name, 'value'], undefined);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={6}>
|
||||||
|
<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 type = filters.find(f => f.name === name)?.type;
|
||||||
|
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
|
||||||
|
key={`${index}value`}
|
||||||
|
label={t('reportcenter.labels.advanced_filters_filter_value')}
|
||||||
|
name={[field.name, "value"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
(() => {
|
||||||
|
const generateReflections = (reflector) => {
|
||||||
|
if (!reflector) return [];
|
||||||
|
|
||||||
|
const {name} = reflector;
|
||||||
|
const path = name?.split('.');
|
||||||
|
const upperPath = path?.[0];
|
||||||
|
const finalPath = path?.slice(1).join('.');
|
||||||
|
|
||||||
|
return generateInternalReflections({
|
||||||
|
bodyshop,
|
||||||
|
upperPath,
|
||||||
|
finalPath
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const reflections = reflector ? generateReflections(reflector) : [];
|
||||||
|
const fieldPath = [[field.name, "value"]];
|
||||||
|
// We have reflections so we will use a select box
|
||||||
|
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 (
|
||||||
|
<Select
|
||||||
|
options={reflections}
|
||||||
|
getPopupContainer={trigger => trigger.parentNode}
|
||||||
|
onChange={(value) => {
|
||||||
|
form.setFieldValue(fieldPath, value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a type of number, so we will use a number input
|
||||||
|
if (type === "number") {
|
||||||
|
return (
|
||||||
|
<InputNumber
|
||||||
|
disabled={!operator}
|
||||||
|
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 (
|
||||||
|
<Input
|
||||||
|
disabled={!operator}
|
||||||
|
onChange={(e) => form.setFieldValue(fieldPath, e.target.value)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
</Form.Item>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
</Col>
|
||||||
|
<Col span={2}>
|
||||||
|
<DeleteFilled
|
||||||
|
style={{margin: "1rem", paddingTop: '23px'}}
|
||||||
|
onClick={() => {
|
||||||
|
remove(field.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
add();
|
||||||
|
}}
|
||||||
|
style={{width: "100%"}}
|
||||||
|
>
|
||||||
|
{t("general.actions.add")}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorters Section
|
||||||
|
* @param sorters
|
||||||
|
* @param form
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function SortersSection({sorters}) {
|
||||||
|
const {t} = useTranslation();
|
||||||
|
return (
|
||||||
|
<Card type='inner' title={t('reportcenter.labels.advanced_filters_sorters')} style={{marginTop: '10px'}}>
|
||||||
|
<Form.List name={["sorters"]}>
|
||||||
|
{(fields, {add, remove}) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Sorters
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<Form.Item key={field.key}>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={11}>
|
||||||
|
<Form.Item
|
||||||
|
key={`${index}field`}
|
||||||
|
label={t('reportcenter.labels.advanced_filters_sorter_field')}
|
||||||
|
name={[field.name, "field"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
options={
|
||||||
|
sorters.map((f) => ({
|
||||||
|
value: f.name,
|
||||||
|
label: f?.translation ? (t(f.translation) === f.translation ? f.label : t(f.translation)) : f.label,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
getPopupContainer={trigger => trigger.parentNode}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={11}>
|
||||||
|
<Form.Item
|
||||||
|
key={`${index}direction`}
|
||||||
|
label={t('reportcenter.labels.advanced_filters_sorter_direction')}
|
||||||
|
name={[field.name, "direction"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
options={getOrderOperatorsByType()}
|
||||||
|
getPopupContainer={trigger => trigger.parentNode}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col span={2}>
|
||||||
|
<DeleteFilled
|
||||||
|
style={{margin: "1rem", paddingTop: '23px'}}
|
||||||
|
onClick={() => {
|
||||||
|
remove(field.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
add();
|
||||||
|
}}
|
||||||
|
style={{width: "100%"}}
|
||||||
|
>
|
||||||
|
{t("general.actions.add")}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render Filters
|
||||||
|
* @param templateId
|
||||||
|
* @param form
|
||||||
|
* @param bodyshop
|
||||||
|
* @returns {JSX.Element|null}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function RenderFilters({templateId, form, bodyshop}) {
|
||||||
const [state, setState] = useState(null);
|
const [state, setState] = useState(null);
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const {t} = useTranslation();
|
const {t} = useTranslation();
|
||||||
|
|
||||||
useEffect(() => {
|
const fetch = useCallback(async () => {
|
||||||
const fetch = async () => {
|
// Reset all the filters and Sorters.
|
||||||
setIsLoading(true);
|
form.resetFields(['filters']);
|
||||||
const data = await fetchFilterData({name: templateId});
|
form.resetFields(['sorters']);
|
||||||
if (data?.success) {
|
form.resetFields(['defaultSorters']);
|
||||||
setState(data.data);
|
|
||||||
} else {
|
|
||||||
setState(null);
|
|
||||||
}
|
|
||||||
setIsLoading(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
const data = await fetchFilterData({name: templateId});
|
||||||
|
|
||||||
|
// We have Success
|
||||||
|
if (data?.success) {
|
||||||
|
if (data?.data?.sorters && data?.data?.sorters.length > 0) {
|
||||||
|
const defaultSorters = data?.data?.sorters.filter((sorter) => sorter.hasOwnProperty('default')).map((sorter) => {
|
||||||
|
return {
|
||||||
|
field: sorter.name,
|
||||||
|
direction: sorter.default.direction
|
||||||
|
};
|
||||||
|
}).sort((a, b) => a.default.order - b.default.order);
|
||||||
|
|
||||||
|
form.setFieldValue('defaultSorters', JSON.stringify(defaultSorters));
|
||||||
|
}
|
||||||
|
// Set the state
|
||||||
|
setState(data.data);
|
||||||
|
}
|
||||||
|
// Something went wrong fetching filter data
|
||||||
|
else {
|
||||||
|
setState(null);
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
}, [templateId, form]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
if (templateId) {
|
if (templateId) {
|
||||||
fetch();
|
fetch();
|
||||||
|
|
||||||
}
|
}
|
||||||
}, [templateId]);
|
}, [templateId, fetch]);
|
||||||
|
|
||||||
|
const filters = useMemo(() => state?.filters || [], [state]);
|
||||||
|
const sorters = useMemo(() => state?.sorters || [], [state]);
|
||||||
|
|
||||||
// Conditional display of filters and sorters
|
|
||||||
if (!templateId) return null;
|
if (!templateId) return null;
|
||||||
if (isLoading) return <LoadingSkeleton/>;
|
if (isLoading) return <LoadingSkeleton/>;
|
||||||
if (!state) return null;
|
if (!state) return null;
|
||||||
|
|
||||||
// Filters and Sorters data available
|
|
||||||
return (
|
return (
|
||||||
<div style={{marginTop: '10px'}}>
|
<div style={{marginTop: '10px'}}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -56,215 +418,11 @@ function RenderFilters({templateId, form}) {
|
|||||||
/>
|
/>
|
||||||
{visible && (
|
{visible && (
|
||||||
<div>
|
<div>
|
||||||
{state.filters && state.filters.length > 0 && (
|
{filters.length > 0 && (
|
||||||
<Card type='inner' title={ t('reportcenter.labels.advanced_filters_filters')} style={{marginTop: '10px'}}>
|
<FiltersSection filters={filters} form={form} bodyshop={bodyshop}/>
|
||||||
<Form.List name={["filters"]}>
|
|
||||||
{(fields, {add, remove, move}) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{fields.map((field, index) => (
|
|
||||||
<Form.Item key={field.key}>
|
|
||||||
<Row gutter={[16, 16]}>
|
|
||||||
<Col span={10}>
|
|
||||||
<Form.Item
|
|
||||||
key={`${index}field`}
|
|
||||||
label="field"
|
|
||||||
name={[field.name, "field"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
options={
|
|
||||||
state.filters
|
|
||||||
? state.filters.map((f) => {
|
|
||||||
return {
|
|
||||||
value: f.name,
|
|
||||||
label: f?.translation ? (t(f.translation) === f.translation ? f.label : t(f.translation)) : f.label,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
: []
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
<Col span={6}>
|
|
||||||
<Form.Item dependencies={[['filters', field.name, "field"]]}>
|
|
||||||
{
|
|
||||||
() => {
|
|
||||||
const name = form.getFieldValue(['filters', field.name, "field"]);
|
|
||||||
const type = state.filters.find(f => f.name === name)?.type;
|
|
||||||
|
|
||||||
return <Form.Item
|
|
||||||
key={`${index}operator`}
|
|
||||||
label="operator"
|
|
||||||
name={[field.name, "operator"]}
|
|
||||||
dependencies={[]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select options={getOperatorsByType(type)}/>
|
|
||||||
</Form.Item>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
</Col>
|
|
||||||
<Col span={6}>
|
|
||||||
<Form.Item dependencies={[['filters', field.name, "field"]]}>
|
|
||||||
{
|
|
||||||
() => {
|
|
||||||
const name = form.getFieldValue(['filters', field.name, "field"]);
|
|
||||||
const type = state.filters.find(f => f.name === name)?.type;
|
|
||||||
|
|
||||||
return <Form.Item
|
|
||||||
key={`${index}value`}
|
|
||||||
label="value"
|
|
||||||
name={[field.name, "value"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{type === 'number' ?
|
|
||||||
<InputNumber
|
|
||||||
onChange={(value) => {
|
|
||||||
form.setFieldsValue({[field.name]: {value: parseInt(value)}});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
:
|
|
||||||
<Input
|
|
||||||
onChange={(value) => {
|
|
||||||
form.setFieldsValue({[field.name]: {value: value.toString()}});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</Form.Item>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
</Col>
|
|
||||||
<Col span={2}>
|
|
||||||
<DeleteFilled
|
|
||||||
style={{margin: "1rem"}}
|
|
||||||
onClick={() => {
|
|
||||||
remove(field.name);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</Form.Item>
|
|
||||||
))}
|
|
||||||
<Form.Item>
|
|
||||||
<Button
|
|
||||||
type="dashed"
|
|
||||||
onClick={() => {
|
|
||||||
add();
|
|
||||||
}}
|
|
||||||
style={{width: "100%"}}
|
|
||||||
>
|
|
||||||
{t("general.actions.add")}
|
|
||||||
</Button>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.List>
|
|
||||||
|
|
||||||
</Card>
|
|
||||||
)}
|
)}
|
||||||
{state.sorters && state.sorters.length > 0 && (
|
{sorters.length > 0 && (
|
||||||
<Card type='inner' title={ t('reportcenter.labels.advanced_filters_sorters')} style={{marginTop: '10px'}}>
|
<SortersSection sorters={sorters} form={form}/>
|
||||||
<Form.List name={["sorters"]}>
|
|
||||||
{(fields, {add, remove, move}) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
Sorters
|
|
||||||
{fields.map((field, index) => (
|
|
||||||
<Form.Item key={field.key}>
|
|
||||||
<Row gutter={[16, 16]}>
|
|
||||||
<Col span={11}>
|
|
||||||
<Form.Item
|
|
||||||
key={`${index}field`}
|
|
||||||
label="field"
|
|
||||||
name={[field.name, "field"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
options={
|
|
||||||
state.sorters
|
|
||||||
? state.sorters.map((f) => ({
|
|
||||||
value: f.name,
|
|
||||||
label: f?.translation ? (t(f.translation) === f.translation ? f.label : t(f.translation)) : f.label,
|
|
||||||
}))
|
|
||||||
: []
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
<Col span={11}>
|
|
||||||
<Form.Item
|
|
||||||
key={`${index}direction`}
|
|
||||||
label="direction"
|
|
||||||
name={[field.name, "direction"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
options={[
|
|
||||||
{value: "desc", label: "Descending"},
|
|
||||||
{value: "asc", label: "Ascending"},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
|
|
||||||
<Col span={2}>
|
|
||||||
<DeleteFilled
|
|
||||||
style={{margin: "1rem"}}
|
|
||||||
onClick={() => {
|
|
||||||
remove(field.name);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</Form.Item>
|
|
||||||
))}
|
|
||||||
<Form.Item>
|
|
||||||
<Button
|
|
||||||
type="dashed"
|
|
||||||
onClick={() => {
|
|
||||||
add();
|
|
||||||
}}
|
|
||||||
style={{width: "100%"}}
|
|
||||||
>
|
|
||||||
{t("general.actions.add")}
|
|
||||||
</Button>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.List>
|
|
||||||
</Card>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
import {uniqBy} from "lodash";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get value from path
|
||||||
|
* @param obj
|
||||||
|
* @param path
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
const getValueFromPath = (obj, path) => path.split('.').reduce((prev, curr) => prev?.[curr], obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* Note: This is intended for future functionality
|
||||||
|
* @type {{special: string[], bodyshop: [{name: string, type: string}]}}
|
||||||
|
*/
|
||||||
|
const VALID_INTERNAL_REFLECTIONS = {
|
||||||
|
bodyshop: [
|
||||||
|
{
|
||||||
|
name: 'md_ro_statuses.statuses',
|
||||||
|
type: 'kv-to-v'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate options
|
||||||
|
* @param bodyshop
|
||||||
|
* @param path
|
||||||
|
* @param labelPath
|
||||||
|
* @param valuePath
|
||||||
|
* @returns {{label: *, value: *}[]}
|
||||||
|
*/
|
||||||
|
const generateOptionsFromObject = (bodyshop, path, labelPath, valuePath) => {
|
||||||
|
const options = getValueFromPath(bodyshop, path);
|
||||||
|
return uniqBy(Object.values(options).map((value) => ({
|
||||||
|
label: value[labelPath],
|
||||||
|
value: value[valuePath],
|
||||||
|
})), 'value');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate special reflections
|
||||||
|
* @param bodyshop
|
||||||
|
* @param finalPath
|
||||||
|
* @returns {{label: *, value: *}[]|{label: *, value: *}[]|{label: string, value: *}[]|*[]}
|
||||||
|
*/
|
||||||
|
const generateSpecialReflections = (bodyshop, finalPath) => {
|
||||||
|
switch (finalPath) {
|
||||||
|
// 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':
|
||||||
|
return generateOptionsFromObject(bodyshop, 'md_responsibility_centers.costs', 'name', 'name');
|
||||||
|
// Special case because Categories is an Array, not an Object.
|
||||||
|
case 'categories':
|
||||||
|
return generateOptionsFromArray(bodyshop, 'md_categories');
|
||||||
|
case 'insurance_companies':
|
||||||
|
return generateOptionsFromObject(bodyshop, 'md_ins_cos', 'name', 'name');
|
||||||
|
case 'employee_teams':
|
||||||
|
return generateOptionsFromObject(bodyshop, 'employee_teams', 'name', 'id');
|
||||||
|
// Special case because Employees uses a concatenation of first_name and last_name
|
||||||
|
case 'employees':
|
||||||
|
const employeesOptions = getValueFromPath(bodyshop, 'employees');
|
||||||
|
return uniqBy(Object.values(employeesOptions).map((value) => ({
|
||||||
|
label: `${value.first_name} ${value.last_name}`,
|
||||||
|
value: value.id,
|
||||||
|
})), 'value');
|
||||||
|
case 'last_names':
|
||||||
|
return generateOptionsFromObject(bodyshop, 'employees', 'last_name', 'last_name');
|
||||||
|
case 'first_names':
|
||||||
|
return generateOptionsFromObject(bodyshop, 'employees', 'first_name', 'first_name');
|
||||||
|
case 'job_statuses':
|
||||||
|
const statusOptions = getValueFromPath(bodyshop, 'md_ro_statuses.statuses');
|
||||||
|
return Object.values(statusOptions).map((value) => ({
|
||||||
|
label: value,
|
||||||
|
value
|
||||||
|
}));
|
||||||
|
default:
|
||||||
|
console.error('Invalid Special reflection provided by Report Filters');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate bodyshop reflections
|
||||||
|
* @param bodyshop
|
||||||
|
* @param finalPath
|
||||||
|
* @returns {{label: *, value: *}[]|*[]}
|
||||||
|
*/
|
||||||
|
const generateBodyshopReflections = (bodyshop, finalPath) => {
|
||||||
|
const options = getValueFromPath(bodyshop, finalPath);
|
||||||
|
const reflectionRenderer = VALID_INTERNAL_REFLECTIONS.bodyshop.find(reflection => reflection.name === finalPath);
|
||||||
|
if (reflectionRenderer?.type === 'kv-to-v') {
|
||||||
|
return Object.values(options).map((value) => ({
|
||||||
|
label: value,
|
||||||
|
value
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate internal reflections based on the path and bodyshop
|
||||||
|
* @param bodyshop
|
||||||
|
* @param upperPath
|
||||||
|
* @param finalPath
|
||||||
|
* @returns {{label: *, value: *}[]|[]|{label: *, value: *}[]|{label: string, value: *}[]|{label: *, value: *}[]|*[]}
|
||||||
|
*/
|
||||||
|
const generateInternalReflections = ({bodyshop, upperPath, finalPath}) => {
|
||||||
|
switch (upperPath) {
|
||||||
|
case 'special':
|
||||||
|
return generateSpecialReflections(bodyshop, finalPath);
|
||||||
|
case 'bodyshop':
|
||||||
|
return generateBodyshopReflections(bodyshop, finalPath);
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export {generateInternalReflections}
|
||||||
@@ -18,10 +18,11 @@ import EmployeeSearchSelect from "../employee-search-select/employee-search-sele
|
|||||||
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
||||||
import "./report-center-modal.styles.scss";
|
import "./report-center-modal.styles.scss";
|
||||||
import ReportCenterModalFiltersSortersComponent from "./report-center-modal-filters-sorters-component";
|
import ReportCenterModalFiltersSortersComponent from "./report-center-modal-filters-sorters-component";
|
||||||
|
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
reportCenterModal: selectReportCenter,
|
reportCenterModal: selectReportCenter,
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
@@ -89,22 +90,28 @@ export function ReportCenterModalComponent({reportCenterModal, bodyshop}) {
|
|||||||
const end = values.dates ? values.dates[1] : null;
|
const end = values.dates ? values.dates[1] : null;
|
||||||
const { id } = values;
|
const { id } = values;
|
||||||
|
|
||||||
await GenerateDocument(
|
const templateConfig = {
|
||||||
{
|
|
||||||
name: values.key,
|
name: values.key,
|
||||||
variables: {
|
variables: {
|
||||||
...(start
|
...(start
|
||||||
? { start: moment(start).startOf("day").format("YYYY-MM-DD") }
|
? {start: moment(start).startOf("day").format("YYYY-MM-DD")}
|
||||||
: {}),
|
: {}),
|
||||||
...(end ? { end: moment(end).endOf("day").format("YYYY-MM-DD") } : {}),
|
...(end ? {end: moment(end).endOf("day").format("YYYY-MM-DD")} : {}),
|
||||||
...(start ? { starttz: moment(start).startOf("day") } : {}),
|
...(start ? {starttz: moment(start).startOf("day")} : {}),
|
||||||
...(end ? { endtz: moment(end).endOf("day") } : {}),
|
...(end ? {endtz: moment(end).endOf("day")} : {}),
|
||||||
|
|
||||||
...(id ? { id: id } : {}),
|
...(id ? {id: id} : {}),
|
||||||
},
|
},
|
||||||
filters: values.filters,
|
filters: values.filters,
|
||||||
sorters: values.sorters,
|
sorters: values.sorters,
|
||||||
},
|
};
|
||||||
|
|
||||||
|
if (_.isString(values.defaultSorters) && !_.isEmpty(values.defaultSorters)) {
|
||||||
|
templateConfig.defaultSorters = JSON.parse(values.defaultSorters);
|
||||||
|
}
|
||||||
|
|
||||||
|
await GenerateDocument(
|
||||||
|
templateConfig,
|
||||||
{
|
{
|
||||||
to: values.to,
|
to: values.to,
|
||||||
subject: Templates[values.key]?.subject,
|
subject: Templates[values.key]?.subject,
|
||||||
@@ -142,7 +149,8 @@ export function ReportCenterModalComponent({reportCenterModal, bodyshop}) {
|
|||||||
onChange={(e) => setSearch(e.target.value)}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
value={search}
|
value={search}
|
||||||
/>
|
/>
|
||||||
<Form.Item
|
<Form.Item name="defaultSorters" hidden/>
|
||||||
|
<Form.Item
|
||||||
name="key"
|
name="key"
|
||||||
label={t("reportcenter.labels.key")}
|
label={t("reportcenter.labels.key")}
|
||||||
// className="radio-group-columns"
|
// className="radio-group-columns"
|
||||||
@@ -206,7 +214,7 @@ export function ReportCenterModalComponent({reportCenterModal, bodyshop}) {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<ReportCenterModalFiltersSortersComponent form={form} />
|
<ReportCenterModalFiltersSortersComponent form={form} bodyshop={bodyshop} />
|
||||||
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
|
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
|
||||||
{() => {
|
{() => {
|
||||||
const key = form.getFieldValue("key");
|
const key = form.getFieldValue("key");
|
||||||
@@ -261,6 +269,9 @@ export function ReportCenterModalComponent({reportCenterModal, bodyshop}) {
|
|||||||
{() => {
|
{() => {
|
||||||
const key = form.getFieldValue("key");
|
const key = form.getFieldValue("key");
|
||||||
const datedisable = Templates[key] && Templates[key].datedisable;
|
const datedisable = Templates[key] && Templates[key].datedisable;
|
||||||
|
|
||||||
|
// TODO: MERGE NOTE, Ranges turns to presets in DatePicker.RangePicker
|
||||||
|
|
||||||
if (datedisable !== true) {
|
if (datedisable !== true) {
|
||||||
return (
|
return (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -275,7 +286,7 @@ export function ReportCenterModalComponent({reportCenterModal, bodyshop}) {
|
|||||||
>
|
>
|
||||||
<DatePicker.RangePicker
|
<DatePicker.RangePicker
|
||||||
format="MM/DD/YYYY"
|
format="MM/DD/YYYY"
|
||||||
presets={DatePickerRanges}
|
ranges={DatePickerRanges}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { Button, Card, Dropdown, Form, InputNumber, notification } from "antd";
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Dropdown,
|
||||||
|
Form,
|
||||||
|
InputNumber,
|
||||||
|
notification,
|
||||||
|
Space,
|
||||||
|
} from "antd";
|
||||||
|
import moment from "moment";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { UPDATE_SCOREBOARD_ENTRY } from "../../graphql/scoreboard.queries";
|
import { UPDATE_SCOREBOARD_ENTRY } from "../../graphql/scoreboard.queries";
|
||||||
@@ -13,6 +22,7 @@ export default function ScoreboardEntryEdit({ entry }) {
|
|||||||
|
|
||||||
const handleFinish = async (values) => {
|
const handleFinish = async (values) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
values.date = moment(values.date).format("YYYY-MM-DD");
|
||||||
const result = await updateScoreboardentry({
|
const result = await updateScoreboardentry({
|
||||||
variables: { sbId: entry.id, sbInput: values },
|
variables: { sbId: entry.id, sbInput: values },
|
||||||
});
|
});
|
||||||
@@ -77,13 +87,14 @@ export default function ScoreboardEntryEdit({ entry }) {
|
|||||||
>
|
>
|
||||||
<InputNumber precision={1} />
|
<InputNumber precision={1} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Space wrap>
|
||||||
<Button type="primary" loading={loading} htmlType="submit">
|
<Button type="primary" loading={loading} htmlType="submit">
|
||||||
{t("general.actions.save")}
|
{t("general.actions.save")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => setVisible(false)}>
|
<Button onClick={() => setVisible(false)}>
|
||||||
{t("general.actions.cancel")}
|
{t("general.actions.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
|
</Space>
|
||||||
</Form>
|
</Form>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { SyncOutlined } from "@ant-design/icons";
|
||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import { Button, Card, Input, Modal, Space, Table, Typography } from "antd";
|
import { Button, Card, Input, Modal, Space, Table, Typography } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
@@ -5,12 +6,14 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { QUERY_SCOREBOARD_PAGINATED } from "../../graphql/scoreboard.queries";
|
import { QUERY_SCOREBOARD_PAGINATED } from "../../graphql/scoreboard.queries";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
|
import { pageLimit } from "../../utils/config";
|
||||||
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay, {
|
||||||
|
OwnerNameDisplayFunction,
|
||||||
|
} from "../owner-name-display/owner-name-display.component";
|
||||||
import ScoreboardEntryEdit from "../scoreboard-entry-edit/scoreboard-entry-edit.component";
|
import ScoreboardEntryEdit from "../scoreboard-entry-edit/scoreboard-entry-edit.component";
|
||||||
import ScoreboardRemoveButton from "../scoreboard-remove-button/scorebard-remove-button.component";
|
import ScoreboardRemoveButton from "../scoreboard-remove-button/scorebard-remove-button.component";
|
||||||
import { SyncOutlined } from "@ant-design/icons";
|
|
||||||
import {pageLimit} from "../../utils/config";
|
|
||||||
export default function ScoreboardJobsList({ scoreBoardlist }) {
|
export default function ScoreboardJobsList({ scoreBoardlist }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
@@ -44,6 +47,7 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
|
|||||||
title: t("jobs.fields.ro_number"),
|
title: t("jobs.fields.ro_number"),
|
||||||
dataIndex: "ro_number",
|
dataIndex: "ro_number",
|
||||||
key: "ro_number",
|
key: "ro_number",
|
||||||
|
sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Link to={"/manage/jobs/" + record.job.id}>
|
<Link to={"/manage/jobs/" + record.job.id}>
|
||||||
{record.job.ro_number || t("general.labels.na")}
|
{record.job.ro_number || t("general.labels.na")}
|
||||||
@@ -55,7 +59,11 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
|
|||||||
dataIndex: "owner",
|
dataIndex: "owner",
|
||||||
key: "owner",
|
key: "owner",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
OwnerNameDisplayFunction(a.job),
|
||||||
|
OwnerNameDisplayFunction(b.job)
|
||||||
|
),
|
||||||
render: (text, record) => <OwnerNameDisplay ownerObject={record.job} />,
|
render: (text, record) => <OwnerNameDisplay ownerObject={record.job} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -63,6 +71,15 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
|
|||||||
dataIndex: "vehicle",
|
dataIndex: "vehicle",
|
||||||
key: "vehicle",
|
key: "vehicle",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
`${a.job.v_model_yr || ""} ${a.job.v_make_desc || ""} ${
|
||||||
|
a.job.v_model_desc || ""
|
||||||
|
}`,
|
||||||
|
`${b.job.v_model_yr || ""} ${b.job.v_make_desc || ""} ${
|
||||||
|
b.job.v_model_desc || ""
|
||||||
|
}`
|
||||||
|
),
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<span>{`${record.job.v_model_yr || ""} ${
|
<span>{`${record.job.v_model_yr || ""} ${
|
||||||
record.job.v_make_desc || ""
|
record.job.v_make_desc || ""
|
||||||
@@ -73,17 +90,20 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
|
|||||||
title: t("scoreboard.fields.date"),
|
title: t("scoreboard.fields.date"),
|
||||||
dataIndex: "date",
|
dataIndex: "date",
|
||||||
key: "date",
|
key: "date",
|
||||||
|
sorter: (a, b) => dateSort(a.date, b.date),
|
||||||
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: t("scoreboard.fields.painthrs"),
|
|
||||||
dataIndex: "painthrs",
|
|
||||||
key: "painthrs",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: t("scoreboard.fields.bodyhrs"),
|
title: t("scoreboard.fields.bodyhrs"),
|
||||||
dataIndex: "bodyhrs",
|
dataIndex: "bodyhrs",
|
||||||
key: "bodyhrs",
|
key: "bodyhrs",
|
||||||
|
sorter: (a, b) => Number(a.bodyhrs) - Number(b.bodyhrs),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("scoreboard.fields.painthrs"),
|
||||||
|
dataIndex: "painthrs",
|
||||||
|
key: "painthrs",
|
||||||
|
sorter: (a, b) => Number(a.painthrs) - Number(b.painthrs),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
@@ -104,8 +124,9 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
|
|||||||
visible={state.visible}
|
visible={state.visible}
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
width="80%"
|
width="80%"
|
||||||
|
closable={false}
|
||||||
cancelButtonProps={{ style: { display: "none" } }}
|
cancelButtonProps={{ style: { display: "none" } }}
|
||||||
onCancel={() =>
|
onOk={() =>
|
||||||
setState((state) => ({
|
setState((state) => ({
|
||||||
...state,
|
...state,
|
||||||
visible: false,
|
visible: false,
|
||||||
|
|||||||
@@ -29,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>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export const QUERY_AVAILABLE_CC = gql`
|
|||||||
]
|
]
|
||||||
status: { _eq: "courtesycars.status.in" }
|
status: { _eq: "courtesycars.status.in" }
|
||||||
}
|
}
|
||||||
|
order_by: { fleetnumber: asc }
|
||||||
) {
|
) {
|
||||||
color
|
color
|
||||||
dailycost
|
dailycost
|
||||||
@@ -29,6 +30,7 @@ export const QUERY_AVAILABLE_CC = gql`
|
|||||||
fleetnumber
|
fleetnumber
|
||||||
fuel
|
fuel
|
||||||
id
|
id
|
||||||
|
insuranceexpires
|
||||||
make
|
make
|
||||||
mileage
|
mileage
|
||||||
model
|
model
|
||||||
@@ -57,7 +59,7 @@ export const CHECK_CC_FLEET_NUMBER = gql`
|
|||||||
`;
|
`;
|
||||||
export const QUERY_ALL_CC = gql`
|
export const QUERY_ALL_CC = gql`
|
||||||
query QUERY_ALL_CC {
|
query QUERY_ALL_CC {
|
||||||
courtesycars {
|
courtesycars(order_by: { fleetnumber: asc }) {
|
||||||
color
|
color
|
||||||
created_at
|
created_at
|
||||||
dailycost
|
dailycost
|
||||||
|
|||||||
@@ -26,10 +26,12 @@ import { OwnerNameDisplayFunction } from "../../components/owner-name-display/ow
|
|||||||
import { auth } from "../../firebase/firebase.utils";
|
import { auth } from "../../firebase/firebase.utils";
|
||||||
import { QUERY_JOB_EXPORT_DMS } from "../../graphql/jobs.queries";
|
import { QUERY_JOB_EXPORT_DMS } from "../../graphql/jobs.queries";
|
||||||
import {
|
import {
|
||||||
|
insertAuditTrail,
|
||||||
setBreadcrumbs,
|
setBreadcrumbs,
|
||||||
setSelectedHeader,
|
setSelectedHeader,
|
||||||
} from "../../redux/application/application.actions";
|
} from "../../redux/application/application.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -38,15 +40,17 @@ 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 }) =>
|
||||||
|
dispatch(insertAuditTrail({ jobid, operation })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(DmsContainer);
|
export default connect(mapStateToProps, mapDispatchToProps)(DmsContainer);
|
||||||
|
|
||||||
export const socket = SocketIO(
|
export const socket = SocketIO(
|
||||||
// process.env.NODE_ENV === "production"
|
process.env.NODE_ENV === "production"
|
||||||
// ? process.env.REACT_APP_AXIOS_BASE_API_URL
|
? process.env.REACT_APP_AXIOS_BASE_API_URL
|
||||||
// : window.location.origin,
|
: window.location.origin,
|
||||||
"http://localhost:4000", // for dev testing,
|
// "http://localhost:4000", // for dev testing,
|
||||||
{
|
{
|
||||||
path: "/ws",
|
path: "/ws",
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
@@ -57,7 +61,12 @@ export const socket = SocketIO(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
export function DmsContainer({
|
||||||
|
bodyshop,
|
||||||
|
setBreadcrumbs,
|
||||||
|
setSelectedHeader,
|
||||||
|
insertAuditTrail,
|
||||||
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [logLevel, setLogLevel] = useState("DEBUG");
|
const [logLevel, setLogLevel] = useState("DEBUG");
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
@@ -115,6 +124,10 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
|||||||
notification.success({
|
notification.success({
|
||||||
message: t("jobs.successes.exported"),
|
message: t("jobs.successes.exported"),
|
||||||
});
|
});
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: payload,
|
||||||
|
operation: AuditTrailMapping.jobexported(),
|
||||||
|
});
|
||||||
history.push("/manage/accounting/receivables");
|
history.push("/manage/accounting/receivables");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ export function* insertAuditTrailSaga({
|
|||||||
fields: {
|
fields: {
|
||||||
audit_trail(existingAuditTrail, { readField }) {
|
audit_trail(existingAuditTrail, { readField }) {
|
||||||
const newAuditTrail = cache.writeQuery({
|
const newAuditTrail = cache.writeQuery({
|
||||||
data: data.insert_audit_trail_one,
|
data: data,
|
||||||
query: INSERT_AUDIT_TRAIL,
|
query: INSERT_AUDIT_TRAIL,
|
||||||
variables,
|
variables,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -109,6 +109,7 @@
|
|||||||
"appointmentcancel": "Appointment canceled. Lost Reason: {{lost_sale_reason}}.",
|
"appointmentcancel": "Appointment canceled. Lost Reason: {{lost_sale_reason}}.",
|
||||||
"appointmentinsert": "Appointment created. Appointment Date: {{start}}.",
|
"appointmentinsert": "Appointment created. Appointment Date: {{start}}.",
|
||||||
"assignedlinehours": "Assigned job lines totaling {{hours}} units to {{team}}.",
|
"assignedlinehours": "Assigned job lines totaling {{hours}} units to {{team}}.",
|
||||||
|
"billdeleted": "Bill with invoice number {{invoice_number}} deleted.",
|
||||||
"billposted": "Bill with invoice number {{invoice_number}} posted.",
|
"billposted": "Bill with invoice number {{invoice_number}} posted.",
|
||||||
"billupdated": "Bill with invoice number {{invoice_number}} updated.",
|
"billupdated": "Bill with invoice number {{invoice_number}} updated.",
|
||||||
"failedpayment": "Failed payment attempt.",
|
"failedpayment": "Failed payment attempt.",
|
||||||
@@ -116,6 +117,7 @@
|
|||||||
"jobassignmentremoved": "Employee assignment removed for {{operation}}",
|
"jobassignmentremoved": "Employee assignment removed for {{operation}}",
|
||||||
"jobchecklist": "Checklist type \"{{type}}\" completed. In production set to {{inproduction}}. Status set to {{status}}.",
|
"jobchecklist": "Checklist type \"{{type}}\" completed. In production set to {{inproduction}}. Status set to {{status}}.",
|
||||||
"jobconverted": "Job converted and assigned number {{ro_number}}.",
|
"jobconverted": "Job converted and assigned number {{ro_number}}.",
|
||||||
|
"jobexported": "Job has been exported.",
|
||||||
"jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.",
|
"jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.",
|
||||||
"jobimported": "Job imported.",
|
"jobimported": "Job imported.",
|
||||||
"jobinproductionchange": "Job production status set to {{inproduction}}",
|
"jobinproductionchange": "Job production status set to {{inproduction}}",
|
||||||
@@ -128,7 +130,9 @@
|
|||||||
"jobspartsorder": "Parts order {{order_number}} added to Job.",
|
"jobspartsorder": "Parts order {{order_number}} added to Job.",
|
||||||
"jobspartsreturn": "Parts return {{order_number}} added to Job.",
|
"jobspartsreturn": "Parts return {{order_number}} added to Job.",
|
||||||
"jobstatuschange": "Job status changed to {{status}}.",
|
"jobstatuschange": "Job status changed to {{status}}.",
|
||||||
"jobsupplement": "Job supplement imported."
|
"jobsupplement": "Job supplement imported.",
|
||||||
|
"jobsuspend": "Suspend Toggle set to {{status}}",
|
||||||
|
"jobvoid": "Job has been voided."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"billlines": {
|
"billlines": {
|
||||||
@@ -760,6 +764,7 @@
|
|||||||
"driverinformation": "Driver's Information",
|
"driverinformation": "Driver's Information",
|
||||||
"findcontract": "Find Contract",
|
"findcontract": "Find Contract",
|
||||||
"findermodal": "Contract Finder",
|
"findermodal": "Contract Finder",
|
||||||
|
"insuranceexpired": "The courtesy car insurance expires before the car is expected to return.",
|
||||||
"noteconvertedfrom": "R.O. created from converted Courtesy Car Contract {{agreementnumber}}.",
|
"noteconvertedfrom": "R.O. created from converted Courtesy Car Contract {{agreementnumber}}.",
|
||||||
"populatefromjob": "Populate from Job",
|
"populatefromjob": "Populate from Job",
|
||||||
"rates": "Contract Rates",
|
"rates": "Contract Rates",
|
||||||
@@ -887,6 +892,7 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"bodyhrs": "Body Hrs",
|
"bodyhrs": "Body Hrs",
|
||||||
"dollarsinproduction": "Dollars in Production",
|
"dollarsinproduction": "Dollars in Production",
|
||||||
|
"phone": "Phone",
|
||||||
"prodhrs": "Production Hrs",
|
"prodhrs": "Production Hrs",
|
||||||
"refhrs": "Refinish Hrs"
|
"refhrs": "Refinish Hrs"
|
||||||
},
|
},
|
||||||
@@ -902,8 +908,10 @@
|
|||||||
"productiondollars": "Total Dollars in Production",
|
"productiondollars": "Total Dollars in Production",
|
||||||
"productionhours": "Total Hours in Production",
|
"productionhours": "Total Hours in Production",
|
||||||
"projectedmonthlysales": "Projected Monthly Sales",
|
"projectedmonthlysales": "Projected Monthly Sales",
|
||||||
"scheduledintoday": "Sheduled In Today: {{date}}",
|
"scheduledindate": "Sheduled In Today: {{date}}",
|
||||||
"scheduledouttoday": "Sheduled Out Today: {{date}}"
|
"scheduledintoday": "Sheduled In Today",
|
||||||
|
"scheduledoutdate": "Sheduled Out Today: {{date}}",
|
||||||
|
"scheduledouttoday": "Sheduled Out Today"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dms": {
|
"dms": {
|
||||||
@@ -2674,6 +2682,13 @@
|
|||||||
"advanced_filters_hide": "Hide",
|
"advanced_filters_hide": "Hide",
|
||||||
"advanced_filters_filters": "Filters",
|
"advanced_filters_filters": "Filters",
|
||||||
"advanced_filters_sorters": "Sorters",
|
"advanced_filters_sorters": "Sorters",
|
||||||
|
"advanced_filters_filter_field": "Field",
|
||||||
|
"advanced_filters_sorter_field": "Field",
|
||||||
|
"advanced_filters_true": "True",
|
||||||
|
"advanced_filters_false": "False",
|
||||||
|
"advanced_filters_sorter_direction": "Direction",
|
||||||
|
"advanced_filters_filter_operator": "Operator",
|
||||||
|
"advanced_filters_filter_value": "Value",
|
||||||
"dates": "Dates",
|
"dates": "Dates",
|
||||||
"employee": "Employee",
|
"employee": "Employee",
|
||||||
"filterson": "Filters on {{object}}: {{field}}",
|
"filterson": "Filters on {{object}}: {{field}}",
|
||||||
@@ -2849,6 +2864,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",
|
||||||
@@ -2865,6 +2881,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",
|
||||||
@@ -2872,6 +2889,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)",
|
||||||
|
|||||||
@@ -109,6 +109,7 @@
|
|||||||
"appointmentcancel": "",
|
"appointmentcancel": "",
|
||||||
"appointmentinsert": "",
|
"appointmentinsert": "",
|
||||||
"assignedlinehours": "",
|
"assignedlinehours": "",
|
||||||
|
"billdeleted": "",
|
||||||
"billposted": "",
|
"billposted": "",
|
||||||
"billupdated": "",
|
"billupdated": "",
|
||||||
"failedpayment": "",
|
"failedpayment": "",
|
||||||
@@ -116,6 +117,7 @@
|
|||||||
"jobassignmentremoved": "",
|
"jobassignmentremoved": "",
|
||||||
"jobchecklist": "",
|
"jobchecklist": "",
|
||||||
"jobconverted": "",
|
"jobconverted": "",
|
||||||
|
"jobexported": "",
|
||||||
"jobfieldchanged": "",
|
"jobfieldchanged": "",
|
||||||
"jobimported": "",
|
"jobimported": "",
|
||||||
"jobinproductionchange": "",
|
"jobinproductionchange": "",
|
||||||
@@ -128,7 +130,9 @@
|
|||||||
"jobspartsorder": "",
|
"jobspartsorder": "",
|
||||||
"jobspartsreturn": "",
|
"jobspartsreturn": "",
|
||||||
"jobstatuschange": "",
|
"jobstatuschange": "",
|
||||||
"jobsupplement": ""
|
"jobsupplement": "",
|
||||||
|
"jobsuspend": "",
|
||||||
|
"jobvoid": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"billlines": {
|
"billlines": {
|
||||||
@@ -760,6 +764,7 @@
|
|||||||
"driverinformation": "",
|
"driverinformation": "",
|
||||||
"findcontract": "",
|
"findcontract": "",
|
||||||
"findermodal": "",
|
"findermodal": "",
|
||||||
|
"insuranceexpired": "",
|
||||||
"noteconvertedfrom": "",
|
"noteconvertedfrom": "",
|
||||||
"populatefromjob": "",
|
"populatefromjob": "",
|
||||||
"rates": "",
|
"rates": "",
|
||||||
@@ -887,6 +892,7 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"bodyhrs": "",
|
"bodyhrs": "",
|
||||||
"dollarsinproduction": "",
|
"dollarsinproduction": "",
|
||||||
|
"phone": "",
|
||||||
"prodhrs": "",
|
"prodhrs": "",
|
||||||
"refhrs": ""
|
"refhrs": ""
|
||||||
},
|
},
|
||||||
@@ -902,7 +908,9 @@
|
|||||||
"productiondollars": "",
|
"productiondollars": "",
|
||||||
"productionhours": "",
|
"productionhours": "",
|
||||||
"projectedmonthlysales": "",
|
"projectedmonthlysales": "",
|
||||||
|
"scheduledindate": "",
|
||||||
"scheduledintoday": "",
|
"scheduledintoday": "",
|
||||||
|
"scheduledoutdate": "",
|
||||||
"scheduledouttoday": ""
|
"scheduledouttoday": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2674,6 +2682,13 @@
|
|||||||
"advanced_filters_hide": "",
|
"advanced_filters_hide": "",
|
||||||
"advanced_filters_filters": "",
|
"advanced_filters_filters": "",
|
||||||
"advanced_filters_sorters": "",
|
"advanced_filters_sorters": "",
|
||||||
|
"advanced_filters_filter_field": "",
|
||||||
|
"advanced_filters_sorter_field": "",
|
||||||
|
"advanced_filters_true": "",
|
||||||
|
"advanced_filters_false": "",
|
||||||
|
"advanced_filters_sorter_direction": "",
|
||||||
|
"advanced_filters_filter_operator": "",
|
||||||
|
"advanced_filters_filter_value": "",
|
||||||
"dates": "",
|
"dates": "",
|
||||||
"employee": "",
|
"employee": "",
|
||||||
"filterson": "",
|
"filterson": "",
|
||||||
@@ -2849,6 +2864,7 @@
|
|||||||
"allemployeetimetickets": "",
|
"allemployeetimetickets": "",
|
||||||
"asoftodaytarget": "",
|
"asoftodaytarget": "",
|
||||||
"body": "",
|
"body": "",
|
||||||
|
"bodyabbrev": "",
|
||||||
"bodycharttitle": "",
|
"bodycharttitle": "",
|
||||||
"calendarperiod": "",
|
"calendarperiod": "",
|
||||||
"combinedcharttitle": "",
|
"combinedcharttitle": "",
|
||||||
@@ -2865,6 +2881,7 @@
|
|||||||
"productivestatistics": "",
|
"productivestatistics": "",
|
||||||
"productivetimeticketsoverdate": "",
|
"productivetimeticketsoverdate": "",
|
||||||
"refinish": "",
|
"refinish": "",
|
||||||
|
"refinishabbrev": "",
|
||||||
"refinishcharttitle": "",
|
"refinishcharttitle": "",
|
||||||
"targets": "",
|
"targets": "",
|
||||||
"thismonth": "",
|
"thismonth": "",
|
||||||
@@ -2872,6 +2889,7 @@
|
|||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"timeticketsemployee": "",
|
"timeticketsemployee": "",
|
||||||
"todateactual": "",
|
"todateactual": "",
|
||||||
|
"total": "",
|
||||||
"totalhrs": "",
|
"totalhrs": "",
|
||||||
"totaloverperiod": "",
|
"totaloverperiod": "",
|
||||||
"weeklyactual": "",
|
"weeklyactual": "",
|
||||||
|
|||||||
@@ -109,6 +109,7 @@
|
|||||||
"appointmentcancel": "",
|
"appointmentcancel": "",
|
||||||
"appointmentinsert": "",
|
"appointmentinsert": "",
|
||||||
"assignedlinehours": "",
|
"assignedlinehours": "",
|
||||||
|
"billdeleted": "",
|
||||||
"billposted": "",
|
"billposted": "",
|
||||||
"billupdated": "",
|
"billupdated": "",
|
||||||
"failedpayment": "",
|
"failedpayment": "",
|
||||||
@@ -116,6 +117,7 @@
|
|||||||
"jobassignmentremoved": "",
|
"jobassignmentremoved": "",
|
||||||
"jobchecklist": "",
|
"jobchecklist": "",
|
||||||
"jobconverted": "",
|
"jobconverted": "",
|
||||||
|
"jobexported": "",
|
||||||
"jobfieldchanged": "",
|
"jobfieldchanged": "",
|
||||||
"jobimported": "",
|
"jobimported": "",
|
||||||
"jobinproductionchange": "",
|
"jobinproductionchange": "",
|
||||||
@@ -128,7 +130,9 @@
|
|||||||
"jobspartsorder": "",
|
"jobspartsorder": "",
|
||||||
"jobspartsreturn": "",
|
"jobspartsreturn": "",
|
||||||
"jobstatuschange": "",
|
"jobstatuschange": "",
|
||||||
"jobsupplement": ""
|
"jobsupplement": "",
|
||||||
|
"jobsuspend": "",
|
||||||
|
"jobvoid": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"billlines": {
|
"billlines": {
|
||||||
@@ -760,6 +764,7 @@
|
|||||||
"driverinformation": "",
|
"driverinformation": "",
|
||||||
"findcontract": "",
|
"findcontract": "",
|
||||||
"findermodal": "",
|
"findermodal": "",
|
||||||
|
"insuranceexpired": "",
|
||||||
"noteconvertedfrom": "",
|
"noteconvertedfrom": "",
|
||||||
"populatefromjob": "",
|
"populatefromjob": "",
|
||||||
"rates": "",
|
"rates": "",
|
||||||
@@ -887,6 +892,7 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"bodyhrs": "",
|
"bodyhrs": "",
|
||||||
"dollarsinproduction": "",
|
"dollarsinproduction": "",
|
||||||
|
"phone": "",
|
||||||
"prodhrs": "",
|
"prodhrs": "",
|
||||||
"refhrs": ""
|
"refhrs": ""
|
||||||
},
|
},
|
||||||
@@ -902,7 +908,9 @@
|
|||||||
"productiondollars": "",
|
"productiondollars": "",
|
||||||
"productionhours": "",
|
"productionhours": "",
|
||||||
"projectedmonthlysales": "",
|
"projectedmonthlysales": "",
|
||||||
|
"scheduledindate": "",
|
||||||
"scheduledintoday": "",
|
"scheduledintoday": "",
|
||||||
|
"scheduledoutdate": "",
|
||||||
"scheduledouttoday": ""
|
"scheduledouttoday": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2674,6 +2682,13 @@
|
|||||||
"advanced_filters_hide": "",
|
"advanced_filters_hide": "",
|
||||||
"advanced_filters_filters": "",
|
"advanced_filters_filters": "",
|
||||||
"advanced_filters_sorters": "",
|
"advanced_filters_sorters": "",
|
||||||
|
"advanced_filters_filter_field": "",
|
||||||
|
"advanced_filters_sorter_field": "",
|
||||||
|
"advanced_filters_true": "",
|
||||||
|
"advanced_filters_false": "",
|
||||||
|
"advanced_filters_sorter_direction": "",
|
||||||
|
"advanced_filters_filter_operator": "",
|
||||||
|
"advanced_filters_filter_value": "",
|
||||||
"dates": "",
|
"dates": "",
|
||||||
"employee": "",
|
"employee": "",
|
||||||
"filterson": "",
|
"filterson": "",
|
||||||
@@ -2849,6 +2864,7 @@
|
|||||||
"allemployeetimetickets": "",
|
"allemployeetimetickets": "",
|
||||||
"asoftodaytarget": "",
|
"asoftodaytarget": "",
|
||||||
"body": "",
|
"body": "",
|
||||||
|
"bodyabbrev": "",
|
||||||
"bodycharttitle": "",
|
"bodycharttitle": "",
|
||||||
"calendarperiod": "",
|
"calendarperiod": "",
|
||||||
"combinedcharttitle": "",
|
"combinedcharttitle": "",
|
||||||
@@ -2865,6 +2881,7 @@
|
|||||||
"productivestatistics": "",
|
"productivestatistics": "",
|
||||||
"productivetimeticketsoverdate": "",
|
"productivetimeticketsoverdate": "",
|
||||||
"refinish": "",
|
"refinish": "",
|
||||||
|
"refinishabbrev": "",
|
||||||
"refinishcharttitle": "",
|
"refinishcharttitle": "",
|
||||||
"targets": "",
|
"targets": "",
|
||||||
"thismonth": "",
|
"thismonth": "",
|
||||||
@@ -2872,6 +2889,7 @@
|
|||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"timeticketsemployee": "",
|
"timeticketsemployee": "",
|
||||||
"todateactual": "",
|
"todateactual": "",
|
||||||
|
"total": "",
|
||||||
"totalhrs": "",
|
"totalhrs": "",
|
||||||
"totaloverperiod": "",
|
"totaloverperiod": "",
|
||||||
"weeklyactual": "",
|
"weeklyactual": "",
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ const AuditTrailMapping = {
|
|||||||
i18n.t("audit_trail.messages.appointmentcancel", { lost_sale_reason }),
|
i18n.t("audit_trail.messages.appointmentcancel", { lost_sale_reason }),
|
||||||
appointmentinsert: (start) =>
|
appointmentinsert: (start) =>
|
||||||
i18n.t("audit_trail.messages.appointmentinsert", { start }),
|
i18n.t("audit_trail.messages.appointmentinsert", { start }),
|
||||||
|
billdeleted: (invoice_number) =>
|
||||||
|
i18n.t("audit_trail.messages.billdeleted", { invoice_number }),
|
||||||
billposted: (invoice_number) =>
|
billposted: (invoice_number) =>
|
||||||
i18n.t("audit_trail.messages.billposted", { invoice_number }),
|
i18n.t("audit_trail.messages.billposted", { invoice_number }),
|
||||||
billupdated: (invoice_number) =>
|
billupdated: (invoice_number) =>
|
||||||
@@ -26,6 +28,7 @@ const AuditTrailMapping = {
|
|||||||
i18n.t("audit_trail.messages.jobchecklist", { type, inproduction, status }),
|
i18n.t("audit_trail.messages.jobchecklist", { type, inproduction, status }),
|
||||||
jobconverted: (ro_number) =>
|
jobconverted: (ro_number) =>
|
||||||
i18n.t("audit_trail.messages.jobconverted", { ro_number }),
|
i18n.t("audit_trail.messages.jobconverted", { ro_number }),
|
||||||
|
jobexported: () => i18n.t("audit_trail.messages.jobexported"),
|
||||||
jobfieldchange: (field, value) =>
|
jobfieldchange: (field, value) =>
|
||||||
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
|
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
|
||||||
jobimported: () => i18n.t("audit_trail.messages.jobimported"),
|
jobimported: () => i18n.t("audit_trail.messages.jobimported"),
|
||||||
@@ -53,6 +56,8 @@ const AuditTrailMapping = {
|
|||||||
jobstatuschange: (status) =>
|
jobstatuschange: (status) =>
|
||||||
i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
||||||
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
|
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
|
||||||
|
jobsuspend: (status) => i18n.t("audit_trail.messages.jobsuspend", { status }),
|
||||||
|
jobvoid: () => i18n.t("audit_trail.messages.jobvoid"),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AuditTrailMapping;
|
export default AuditTrailMapping;
|
||||||
|
|||||||
@@ -24,4 +24,13 @@ const range = {
|
|||||||
],
|
],
|
||||||
"Last 90 Days": [moment().add(-90, "days"), moment()],
|
"Last 90 Days": [moment().add(-90, "days"), moment()],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// We are development, lets get crazy
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
range["Last year"] = [
|
||||||
|
moment().subtract(1, "year"),
|
||||||
|
moment(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
export default range;
|
export default range;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {store} from "../redux/store";
|
|||||||
import client from "../utils/GraphQLClient";
|
import client from "../utils/GraphQLClient";
|
||||||
import cleanAxios from "./CleanAxios";
|
import cleanAxios from "./CleanAxios";
|
||||||
import {TemplateList} from "./TemplateConstants";
|
import {TemplateList} from "./TemplateConstants";
|
||||||
import {applyFilters, applySorters, parseQuery, printQuery, wrapFiltersInAnd} from "./graphQLmodifier";
|
import {generateTemplate} from "./graphQLmodifier";
|
||||||
|
|
||||||
const server = process.env.REACT_APP_REPORTS_SERVER_URL;
|
const server = process.env.REACT_APP_REPORTS_SERVER_URL;
|
||||||
|
|
||||||
@@ -75,7 +75,10 @@ export default async function RenderTemplate(
|
|||||||
headerpath: `/${bodyshop.imexshopid}/header.html`,
|
headerpath: `/${bodyshop.imexshopid}/header.html`,
|
||||||
footerpath: `/${bodyshop.imexshopid}/footer.html`,
|
footerpath: `/${bodyshop.imexshopid}/footer.html`,
|
||||||
bodyshop: bodyshop,
|
bodyshop: bodyshop,
|
||||||
|
filters: templateObject?.filters,
|
||||||
|
sorters: templateObject?.sorters,
|
||||||
offset: bodyshop.timezone, //dayjs().utcOffset(),
|
offset: bodyshop.timezone, //dayjs().utcOffset(),
|
||||||
|
defaultSorters: templateObject?.defaultSorters,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -278,7 +281,9 @@ export const GenerateDocument = async (
|
|||||||
sendType,
|
sendType,
|
||||||
jobid
|
jobid
|
||||||
) => {
|
) => {
|
||||||
|
|
||||||
const bodyshop = store.getState().user.bodyshop;
|
const bodyshop = store.getState().user.bodyshop;
|
||||||
|
|
||||||
if (sendType === "e") {
|
if (sendType === "e") {
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
setEmailOptions({
|
setEmailOptions({
|
||||||
@@ -402,9 +407,12 @@ const fetchContextData = async (templateObject, jsrAuth) => {
|
|||||||
// console.log('Unmodified Query');
|
// console.log('Unmodified Query');
|
||||||
// console.dir(templateQueryToExecute);
|
// console.dir(templateQueryToExecute);
|
||||||
|
|
||||||
|
const hasFilters = templateObject?.filters?.length > 0;
|
||||||
|
const hasSorters = templateObject?.sorters?.length > 0;
|
||||||
|
const hasDefaultSorters = templateObject?.defaultSorters?.length > 0;
|
||||||
|
|
||||||
// We have no template filters or sorters, so we can just execute the query and return the data
|
// We have no template filters or sorters, so we can just execute the query and return the data
|
||||||
if ((!templateObject?.filters && !templateObject?.filters?.length && !templateObject?.sorters && !templateObject?.sorters?.length)) {
|
if (!hasFilters && !hasSorters && !hasDefaultSorters) {
|
||||||
let contextData = {};
|
let contextData = {};
|
||||||
if (templateQueryToExecute) {
|
if (templateQueryToExecute) {
|
||||||
const {data} = await client.query({
|
const {data} = await client.query({
|
||||||
@@ -417,36 +425,11 @@ const fetchContextData = async (templateObject, jsrAuth) => {
|
|||||||
return {contextData, useShopSpecificTemplate};
|
return {contextData, useShopSpecificTemplate};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the query and apply the filters and sorters
|
return await generateTemplate(
|
||||||
const ast = parseQuery(templateQueryToExecute);
|
templateQueryToExecute,
|
||||||
|
templateObject,
|
||||||
let filterFields = [];
|
useShopSpecificTemplate
|
||||||
|
);
|
||||||
if (templateObject?.filters && templateObject?.filters?.length) {
|
|
||||||
applyFilters(ast, templateObject.filters, filterFields);
|
|
||||||
wrapFiltersInAnd(ast, filterFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (templateObject?.sorters && templateObject?.sorters?.length) {
|
|
||||||
applySorters(ast, templateObject.sorters);
|
|
||||||
}
|
|
||||||
|
|
||||||
const finalQuery = printQuery(ast);
|
|
||||||
|
|
||||||
// commented out for future revision debugging
|
|
||||||
// console.log('Modified Query');
|
|
||||||
// console.log(finalQuery);
|
|
||||||
|
|
||||||
let contextData = {};
|
|
||||||
if (templateQueryToExecute) {
|
|
||||||
const {data} = await client.query({
|
|
||||||
query: gql(finalQuery),
|
|
||||||
variables: {...templateObject.variables},
|
|
||||||
});
|
|
||||||
contextData = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {contextData, useShopSpecificTemplate};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//export const displayTemplateInWindow = (html) => {
|
//export const displayTemplateInWindow = (html) => {
|
||||||
|
|||||||
@@ -1,32 +1,96 @@
|
|||||||
import {Kind, parse, print, visit} from "graphql";
|
import {Kind, parse, print, visit} from "graphql";
|
||||||
|
import client from "./GraphQLClient";
|
||||||
|
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"}
|
||||||
];
|
];
|
||||||
|
|
||||||
export function getOperatorsByType(type = 'string') {
|
/**
|
||||||
|
* The available operators for sorting
|
||||||
|
* @type {[{label: string, value: string},{label: string, value: string}]}
|
||||||
|
*/
|
||||||
|
const ORDER_BY_OPERATORS = [
|
||||||
|
{value: "asc", label: "ascending"},
|
||||||
|
{value: "desc", label: "descending"}
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the available operators for filtering
|
||||||
|
* @returns {[{label: string, value: string},{label: string, value: string}]}
|
||||||
|
*/
|
||||||
|
export function getOrderOperatorsByType() {
|
||||||
|
return ORDER_BY_OPERATORS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the available operators for filtering
|
||||||
|
* @param type
|
||||||
|
* @returns {[{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},{label: string, value: string},null]}
|
||||||
|
*/
|
||||||
|
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
|
||||||
@@ -44,6 +108,49 @@ export function parseQuery(query) {
|
|||||||
export function printQuery(query) {
|
export function printQuery(query) {
|
||||||
return print(query);
|
return print(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a template based on the query and object
|
||||||
|
* @param templateQueryToExecute
|
||||||
|
* @param templateObject
|
||||||
|
* @param useShopSpecificTemplate
|
||||||
|
* @returns {Promise<{contextData: {}, useShopSpecificTemplate}>}
|
||||||
|
*/
|
||||||
|
export async function generateTemplate(templateQueryToExecute, templateObject, useShopSpecificTemplate) {
|
||||||
|
// Advanced Filtering and Sorting modifications start here
|
||||||
|
|
||||||
|
// Parse the query and apply the filters and sorters
|
||||||
|
const ast = parseQuery(templateQueryToExecute);
|
||||||
|
|
||||||
|
|
||||||
|
if (templateObject?.filters && templateObject?.filters?.length) {
|
||||||
|
applyFilters(ast, templateObject.filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (templateObject?.sorters && templateObject?.sorters?.length) {
|
||||||
|
applySorters(ast, templateObject.sorters);
|
||||||
|
} else if (templateObject?.defaultSorters && templateObject?.defaultSorters?.length) {
|
||||||
|
applySorters(ast, templateObject.defaultSorters);
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalQuery = printQuery(ast);
|
||||||
|
|
||||||
|
// commented out for future revision debugging
|
||||||
|
// console.log('Modified Query');
|
||||||
|
// console.log(finalQuery);
|
||||||
|
|
||||||
|
let contextData = {};
|
||||||
|
if (templateQueryToExecute) {
|
||||||
|
const {data} = await client.query({
|
||||||
|
query: gql(finalQuery),
|
||||||
|
variables: {...templateObject.variables},
|
||||||
|
});
|
||||||
|
contextData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {contextData, useShopSpecificTemplate};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply sorters to the AST
|
* Apply sorters to the AST
|
||||||
* @param ast
|
* @param ast
|
||||||
@@ -83,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
|
||||||
@@ -104,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, {
|
||||||
@@ -116,152 +272,179 @@ 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
|
||||||
@@ -269,41 +452,17 @@ export function applyFilters(ast, filters) {
|
|||||||
* @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 */
|
|
||||||
|
|||||||
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",
|
||||||
@@ -53,6 +54,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"
|
||||||
},
|
},
|
||||||
@@ -696,11 +698,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"
|
||||||
},
|
},
|
||||||
@@ -719,6 +1206,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",
|
||||||
@@ -1035,6 +1613,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",
|
||||||
@@ -1107,6 +1733,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",
|
||||||
@@ -1715,6 +2349,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",
|
||||||
@@ -1858,6 +2515,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",
|
||||||
@@ -1907,6 +2573,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",
|
||||||
@@ -3308,6 +3982,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",
|
||||||
@@ -3855,6 +4537,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",
|
||||||
@@ -4447,6 +5138,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",
|
||||||
@@ -4460,6 +5157,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",
|
||||||
@@ -4541,6 +5244,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",
|
||||||
@@ -5458,6 +6173,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",
|
||||||
@@ -6900,6 +7623,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",
|
||||||
@@ -6955,6 +7687,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",
|
||||||
@@ -62,6 +63,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"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user