Compare commits
26 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
790ab0447f | ||
|
|
3737fe457f | ||
|
|
bb4e8eb5bd | ||
|
|
27a07e8d5d | ||
|
|
e2618eee83 | ||
|
|
66c51a4be5 | ||
|
|
d5afcaeaab | ||
|
|
c332ec11b7 | ||
|
|
cf31290f05 | ||
|
|
dbbab910b6 | ||
|
|
abf01b4966 | ||
|
|
a965f9edf5 | ||
|
|
f02ca05eba | ||
|
|
a182ea0869 | ||
|
|
7bc2d41a68 | ||
|
|
5277e90946 | ||
|
|
15ea4e6afa | ||
|
|
5b3b6a409c | ||
|
|
d92bab113e | ||
|
|
93c6e2b601 | ||
|
|
19a90571f6 | ||
|
|
736e9cedfa | ||
|
|
c433103e1b | ||
|
|
2892fdbb58 | ||
|
|
c45f38e47b | ||
|
|
54a58c9fbc |
@@ -65429,6 +65429,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>tags</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>taxid</name>
|
<name>taxid</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Card, Checkbox, Input, Space, Table } from "antd";
|
import { Card, Checkbox, Input, Space, Table } from "antd";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React, { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
@@ -16,12 +16,13 @@ import PayableExportAll from "../payable-export-all-button/payable-export-all-bu
|
|||||||
import PayableExportButton from "../payable-export-button/payable-export-button.component";
|
import PayableExportButton from "../payable-export-button/payable-export-button.component";
|
||||||
import BillMarkSelectedExported from "../payable-mark-selected-exported/payable-mark-selected-exported.component";
|
import BillMarkSelectedExported from "../payable-mark-selected-exported/payable-mark-selected-exported.component";
|
||||||
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
||||||
|
import useLocalStorage from "./../../utils/useLocalStorage";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = () => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills, ref
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [selectedBills, setSelectedBills] = useState([]);
|
const [selectedBills, setSelectedBills] = useState([]);
|
||||||
const [transInProgress, setTransInProgress] = useState(false);
|
const [transInProgress, setTransInProgress] = useState(false);
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useLocalStorage("accounting-payables-table-state", {
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
search: ""
|
search: ""
|
||||||
});
|
});
|
||||||
@@ -181,7 +182,7 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills, ref
|
|||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
rowSelection={{
|
rowSelection={{
|
||||||
onSelectAll: (selected, selectedRows) => setSelectedBills(selectedRows.map((i) => i.id)),
|
onSelectAll: (selected, selectedRows) => setSelectedBills(selectedRows.map((i) => i.id)),
|
||||||
onSelect: (record, selected, selectedRows, nativeEvent) => {
|
onSelect: (record, selected, selectedRows) => {
|
||||||
setSelectedBills(selectedRows.map((i) => i.id));
|
setSelectedBills(selectedRows.map((i) => i.id));
|
||||||
},
|
},
|
||||||
getCheckboxProps: (record) => ({
|
getCheckboxProps: (record) => ({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Card, Input, Space, Table } from "antd";
|
import { Card, Input, Space, Table } from "antd";
|
||||||
import React, { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
@@ -10,6 +10,7 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
|||||||
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
import { exportPageLimit } from "../../utils/config";
|
import { exportPageLimit } from "../../utils/config";
|
||||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
|
import useLocalStorage from "../../utils/useLocalStorage";
|
||||||
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
||||||
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||||
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
|
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
|
||||||
@@ -21,7 +22,7 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = () => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, payments,
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [selectedPayments, setSelectedPayments] = useState([]);
|
const [selectedPayments, setSelectedPayments] = useState([]);
|
||||||
const [transInProgress, setTransInProgress] = useState(false);
|
const [transInProgress, setTransInProgress] = useState(false);
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useLocalStorage("accounting-payments-table-state", {
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
search: ""
|
search: ""
|
||||||
});
|
});
|
||||||
@@ -194,7 +195,7 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, payments,
|
|||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
rowSelection={{
|
rowSelection={{
|
||||||
onSelectAll: (selected, selectedRows) => setSelectedPayments(selectedRows.map((i) => i.id)),
|
onSelectAll: (selected, selectedRows) => setSelectedPayments(selectedRows.map((i) => i.id)),
|
||||||
onSelect: (record, selected, selectedRows, nativeEvent) => {
|
onSelect: (record, selected, selectedRows) => {
|
||||||
setSelectedPayments(selectedRows.map((i) => i.id));
|
setSelectedPayments(selectedRows.map((i) => i.id));
|
||||||
},
|
},
|
||||||
getCheckboxProps: (record) => ({
|
getCheckboxProps: (record) => ({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Button, Card, Input, Space, Table } from "antd";
|
import { Button, Card, Input, Space, Table } from "antd";
|
||||||
import React, { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
@@ -10,6 +10,7 @@ import { exportPageLimit } from "../../utils/config";
|
|||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
|
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
|
||||||
|
import useLocalStorage from "../../utils/useLocalStorage";
|
||||||
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
||||||
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
|
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
|
||||||
import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-button.component";
|
import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-button.component";
|
||||||
@@ -20,7 +21,7 @@ import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
|||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = () => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(AccountingReceivablesTableComponent);
|
export default connect(mapStateToProps, mapDispatchToProps)(AccountingReceivablesTableComponent);
|
||||||
@@ -30,7 +31,7 @@ export function AccountingReceivablesTableComponent({ bodyshop, loading, jobs, r
|
|||||||
const [selectedJobs, setSelectedJobs] = useState([]);
|
const [selectedJobs, setSelectedJobs] = useState([]);
|
||||||
const [transInProgress, setTransInProgress] = useState(false);
|
const [transInProgress, setTransInProgress] = useState(false);
|
||||||
|
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useLocalStorage("accounting-receivables-table-state", {
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
search: ""
|
search: ""
|
||||||
});
|
});
|
||||||
@@ -207,7 +208,7 @@ export function AccountingReceivablesTableComponent({ bodyshop, loading, jobs, r
|
|||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
rowSelection={{
|
rowSelection={{
|
||||||
onSelectAll: (selected, selectedRows) => setSelectedJobs(selectedRows.map((i) => i.id)),
|
onSelectAll: (selected, selectedRows) => setSelectedJobs(selectedRows.map((i) => i.id)),
|
||||||
onSelect: (record, selected, selectedRows, nativeEvent) => {
|
onSelect: (record, selected, selectedRows) => {
|
||||||
setSelectedJobs(selectedRows.map((i) => i.id));
|
setSelectedJobs(selectedRows.map((i) => i.id));
|
||||||
},
|
},
|
||||||
getCheckboxProps: (record) => ({
|
getCheckboxProps: (record) => ({
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
Typography
|
Typography
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import Dinero from "dinero.js";
|
import Dinero from "dinero.js";
|
||||||
import React from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -24,14 +23,14 @@ import i18n from "../../translations/i18n";
|
|||||||
import dayjs from "../../utils/day";
|
import dayjs from "../../utils/day";
|
||||||
import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component";
|
import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component";
|
||||||
import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component";
|
import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component";
|
||||||
|
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = () => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(DmsPostForm);
|
export default connect(mapStateToProps, mapDispatchToProps)(DmsPostForm);
|
||||||
@@ -93,7 +92,9 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
|
|||||||
})
|
})
|
||||||
: ""
|
: ""
|
||||||
}`.slice(0, 239),
|
}`.slice(0, 239),
|
||||||
inservicedate: dayjs("2019-01-01")
|
inservicedate: dayjs(
|
||||||
|
`${(job.v_model_yr && (job.v_model_yr < 100 ? (job.v_model_yr >= (dayjs().year() + 1) % 100 ? 1900 + parseInt(job.v_model_yr) : 2000 + parseInt(job.v_model_yr)) : job.v_model_yr)) || 2019}-01-01`
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LayoutFormRow grow>
|
<LayoutFormRow grow>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { forwardRef } from "react";
|
import { forwardRef } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const LaborTypeFormItem = ({ value, onChange }, ref) => {
|
const LaborTypeFormItem = ({ value }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
if (!value) return null;
|
if (!value) return null;
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import React, { forwardRef } from "react";
|
import { forwardRef } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const PartTypeFormItem = ({ value, onChange }, ref) => {
|
const PartTypeFormItem = ({ value }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
if (!value) return null;
|
if (!value) return null;
|
||||||
|
|
||||||
return <div>{t(`joblines.fields.part_types.${value}`)}</div>;
|
return (
|
||||||
|
<div style={{ wordWrap: "break-word", overflowWrap: "break-word" }}>{t(`joblines.fields.part_types.${value}`)}</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
export default forwardRef(PartTypeFormItem);
|
export default forwardRef(PartTypeFormItem);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import Dinero from "dinero.js";
|
import Dinero from "dinero.js";
|
||||||
import React, { forwardRef } from "react";
|
import { forwardRef } from "react";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
@@ -8,23 +7,24 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = () => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
|
|
||||||
const ReadOnlyFormItem = ({ bodyshop, value, type = "text", onChange }, ref) => {
|
const ReadOnlyFormItem = ({ bodyshop, value, type = "text" }) => {
|
||||||
if (!value) return null;
|
if (!value) return null;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "employee":
|
case "employee": {
|
||||||
const emp = bodyshop.employees.find((e) => e.id === value);
|
const emp = bodyshop.employees.find((e) => e.id === value);
|
||||||
return `${emp?.first_name} ${emp?.last_name}`;
|
return `${emp?.first_name} ${emp?.last_name}`;
|
||||||
|
}
|
||||||
|
|
||||||
case "text":
|
case "text":
|
||||||
return <div>{value}</div>;
|
return <div style={{ wordWrap: "break-word", overflowWrap: "break-word" }}>{value}</div>;
|
||||||
case "currency":
|
case "currency":
|
||||||
return <div>{Dinero({ amount: Math.round(value * 100) }).toFormat()}</div>;
|
return <div>{Dinero({ amount: Math.round(value * 100) }).toFormat()}</div>;
|
||||||
default:
|
default:
|
||||||
return <div>{value}</div>;
|
return <div style={{ wordWrap: "break-word", overflowWrap: "break-word" }}>{value}</div>;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +1,17 @@
|
|||||||
import { PushpinFilled, PushpinOutlined } from "@ant-design/icons";
|
import { PushpinFilled, PushpinOutlined } from "@ant-design/icons";
|
||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { GET_JOB_BY_PK } from "../../graphql/jobs.queries";
|
|
||||||
import { UPDATE_NOTE } from "../../graphql/notes.queries";
|
import { UPDATE_NOTE } from "../../graphql/notes.queries";
|
||||||
|
|
||||||
function JobNotesPinToggle({ note }) {
|
function JobNotesPinToggle({ note }) {
|
||||||
const [updateNote] = useMutation(UPDATE_NOTE, {
|
const [updateNote] = useMutation(UPDATE_NOTE);
|
||||||
update(cache, { data: { updateNote: updatedNote } }) {
|
|
||||||
try {
|
|
||||||
const existingJob = cache.readQuery({
|
|
||||||
query: GET_JOB_BY_PK,
|
|
||||||
variables: { id: note.jobid }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (existingJob) {
|
|
||||||
cache.writeQuery({
|
|
||||||
query: GET_JOB_BY_PK,
|
|
||||||
variables: { id: note.jobid },
|
|
||||||
data: {
|
|
||||||
...existingJob,
|
|
||||||
job: {
|
|
||||||
...existingJob.job,
|
|
||||||
notes: updatedNote.pinned
|
|
||||||
? [updatedNote, ...existingJob.job.notes]
|
|
||||||
: existingJob.job.notes.filter((n) => n.id !== updatedNote.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Query not yet executed is most likely. No logging as this isn't a fatal error.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const handlePinToggle = () => {
|
const handlePinToggle = () => {
|
||||||
updateNote({
|
updateNote({
|
||||||
variables: {
|
variables: {
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
note: { pinned: !note.pinned }
|
note: { pinned: !note.pinned }
|
||||||
}
|
},
|
||||||
|
refetchQueries: ["GET_JOB_BY_PK", "QUERY_JOB_CARD_DETAILS", "QUERY_PARTS_QUEUE_CARD_DETAILS"]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { DownCircleFilled } from "@ant-design/icons";
|
import { DownCircleFilled } from "@ant-design/icons";
|
||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { Button, Dropdown } from "antd";
|
import { Button, Dropdown } from "antd";
|
||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||||
import { UPDATE_JOB_STATUS } from "../../graphql/jobs.queries";
|
import { UPDATE_JOB_STATUS } from "../../graphql/jobs.queries";
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -24,7 +24,6 @@ export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail }) {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [availableStatuses, setAvailableStatuses] = useState([]);
|
const [availableStatuses, setAvailableStatuses] = useState([]);
|
||||||
const [otherStages, setOtherStages] = useState([]);
|
|
||||||
const [mutationUpdateJobstatus] = useMutation(UPDATE_JOB_STATUS);
|
const [mutationUpdateJobstatus] = useMutation(UPDATE_JOB_STATUS);
|
||||||
const notification = useNotification();
|
const notification = useNotification();
|
||||||
|
|
||||||
@@ -32,7 +31,7 @@ export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail }) {
|
|||||||
mutationUpdateJobstatus({
|
mutationUpdateJobstatus({
|
||||||
variables: { jobId: job.id, status: status }
|
variables: { jobId: job.id, status: status }
|
||||||
})
|
})
|
||||||
.then((r) => {
|
.then(() => {
|
||||||
notification["success"]({ message: t("jobs.successes.save") });
|
notification["success"]({ message: t("jobs.successes.save") });
|
||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
@@ -41,7 +40,7 @@ export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail }) {
|
|||||||
});
|
});
|
||||||
// refetch();
|
// refetch();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(() => {
|
||||||
notification["error"]({ message: t("jobs.errors.saving") });
|
notification["error"]({ message: t("jobs.errors.saving") });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -51,19 +50,14 @@ export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail }) {
|
|||||||
if (job && bodyshop) {
|
if (job && bodyshop) {
|
||||||
if (bodyshop.md_ro_statuses.pre_production_statuses.includes(job.status)) {
|
if (bodyshop.md_ro_statuses.pre_production_statuses.includes(job.status)) {
|
||||||
setAvailableStatuses(bodyshop.md_ro_statuses.pre_production_statuses);
|
setAvailableStatuses(bodyshop.md_ro_statuses.pre_production_statuses);
|
||||||
if (bodyshop.md_ro_statuses.production_statuses[0])
|
|
||||||
setOtherStages([bodyshop.md_ro_statuses.production_statuses[0]]);
|
|
||||||
} else if (bodyshop.md_ro_statuses.production_statuses.includes(job.status)) {
|
} else if (bodyshop.md_ro_statuses.production_statuses.includes(job.status)) {
|
||||||
setAvailableStatuses(bodyshop.md_ro_statuses.production_statuses);
|
setAvailableStatuses(bodyshop.md_ro_statuses.production_statuses);
|
||||||
setOtherStages([bodyshop.md_ro_statuses.default_imported, bodyshop.md_ro_statuses.default_delivered]);
|
|
||||||
} else if (bodyshop.md_ro_statuses.post_production_statuses.includes(job.status)) {
|
} else if (bodyshop.md_ro_statuses.post_production_statuses.includes(job.status)) {
|
||||||
setAvailableStatuses(
|
setAvailableStatuses(
|
||||||
bodyshop.md_ro_statuses.post_production_statuses.filter(
|
bodyshop.md_ro_statuses.post_production_statuses.filter(
|
||||||
(s) => s !== bodyshop.md_ro_statuses.default_invoiced && s !== bodyshop.md_ro_statuses.default_exported
|
(s) => s !== bodyshop.md_ro_statuses.default_invoiced && s !== bodyshop.md_ro_statuses.default_exported
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if (bodyshop.md_ro_statuses.production_statuses[0])
|
|
||||||
setOtherStages([bodyshop.md_ro_statuses.production_statuses[0]]);
|
|
||||||
} else {
|
} else {
|
||||||
console.log("Status didn't match any restrictions. Allowing all status changes.");
|
console.log("Status didn't match any restrictions. Allowing all status changes.");
|
||||||
setAvailableStatuses(bodyshop.md_ro_statuses.statuses);
|
setAvailableStatuses(bodyshop.md_ro_statuses.statuses);
|
||||||
@@ -76,16 +70,7 @@ export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail }) {
|
|||||||
...availableStatuses.map((item) => ({
|
...availableStatuses.map((item) => ({
|
||||||
key: item,
|
key: item,
|
||||||
label: item
|
label: item
|
||||||
})),
|
}))
|
||||||
...(job.converted
|
|
||||||
? [
|
|
||||||
{ type: "divider" },
|
|
||||||
...otherStages.map((item) => ({
|
|
||||||
key: item,
|
|
||||||
label: item
|
|
||||||
}))
|
|
||||||
]
|
|
||||||
: [])
|
|
||||||
],
|
],
|
||||||
onClick: (e) => updateJobStatus(e.key)
|
onClick: (e) => updateJobStatus(e.key)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import { WarningOutlined } from "@ant-design/icons";
|
||||||
import { Form, Select, Space, Tooltip } from "antd";
|
import { Form, Select, Space, Tooltip } from "antd";
|
||||||
import React from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -8,14 +8,13 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
import LaborTypeFormItem from "../form-items-formatted/labor-type-form-item.component";
|
import LaborTypeFormItem from "../form-items-formatted/labor-type-form-item.component";
|
||||||
import PartTypeFormItem from "../form-items-formatted/part-type-form-item.component";
|
import PartTypeFormItem from "../form-items-formatted/part-type-form-item.component";
|
||||||
import ReadOnlyFormItem from "../form-items-formatted/read-only-form-item.component";
|
import ReadOnlyFormItem from "../form-items-formatted/read-only-form-item.component";
|
||||||
import { WarningOutlined } from "@ant-design/icons";
|
|
||||||
import "./jobs-close-lines.styles.scss";
|
import "./jobs-close-lines.styles.scss";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
jobRO: selectJobReadOnly
|
jobRO: selectJobReadOnly
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = () => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -24,7 +23,7 @@ export function JobsCloseLines({ bodyshop, job, jobRO }) {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.List name={["joblines"]}>
|
<Form.List name={["joblines"]}>
|
||||||
{(fields, { add, remove, move }) => {
|
{(fields) => {
|
||||||
return (
|
return (
|
||||||
<table className="jobs-close-table">
|
<table className="jobs-close-table">
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useMutation } from "@apollo/client";
|
import { useApolloClient, useMutation } from "@apollo/client";
|
||||||
import { Form, Modal } from "antd";
|
import { Form, Modal } from "antd";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -43,6 +43,8 @@ export function NoteUpsertModalContainer({ currentUser, noteUpsertModal, toggleM
|
|||||||
|
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
const { client } = useApolloClient();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
//Required to prevent infinite looping.
|
//Required to prevent infinite looping.
|
||||||
if (existingNote && open) {
|
if (existingNote && open) {
|
||||||
@@ -67,34 +69,8 @@ export function NoteUpsertModalContainer({ currentUser, noteUpsertModal, toggleM
|
|||||||
noteId: existingNote.id,
|
noteId: existingNote.id,
|
||||||
note: values
|
note: values
|
||||||
},
|
},
|
||||||
update(cache, { data: { updateNote: updatedNote } }) {
|
refetchQueries: ["GET_JOB_BY_PK", "QUERY_JOB_CARD_DETAILS", "QUERY_PARTS_QUEUE_CARD_DETAILS"]
|
||||||
try {
|
}).then(() => {
|
||||||
const existingJob = cache.readQuery({
|
|
||||||
query: GET_JOB_BY_PK,
|
|
||||||
variables: { id: jobId }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (existingJob) {
|
|
||||||
cache.writeQuery({
|
|
||||||
query: GET_JOB_BY_PK,
|
|
||||||
variables: { id: jobId },
|
|
||||||
data: {
|
|
||||||
...existingJob,
|
|
||||||
job: {
|
|
||||||
...existingJob.job,
|
|
||||||
notes: updatedNote.pinned
|
|
||||||
? [updatedNote, ...existingJob.job.notes]
|
|
||||||
: existingJob.job.notes.filter((n) => n.id !== updatedNote.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Cache miss is okay, query hasn't been executed yet
|
|
||||||
console.log("Cache miss for GET_JOB_BY_PK");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).then((r) => {
|
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("notes.successes.updated")
|
message: t("notes.successes.updated")
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ const ret = {
|
|||||||
"shop:dashboard": 3,
|
"shop:dashboard": 3,
|
||||||
"shop:rbac": 5,
|
"shop:rbac": 5,
|
||||||
"shop:reportcenter": 2,
|
"shop:reportcenter": 2,
|
||||||
|
"shop:responsibilitycenter": 4, // Updated from "shop:responsibility" to "shop:responsibilitycenter"
|
||||||
"shop:templates": 4,
|
"shop:templates": 4,
|
||||||
"shop:vendors": 2,
|
"shop:vendors": 2,
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { useMutation, useQuery } from "@apollo/client";
|
import { useMutation, useQuery } from "@apollo/client";
|
||||||
import { Form } from "antd";
|
import { Form } from "antd";
|
||||||
import dayjs from "../../utils/day";
|
import { useEffect, useState } from "react";
|
||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { QUERY_BODYSHOP, UPDATE_SHOP } from "../../graphql/bodyshop.queries";
|
import { QUERY_BODYSHOP, UPDATE_SHOP } from "../../graphql/bodyshop.queries";
|
||||||
|
import dayjs from "../../utils/day";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import FormsFieldChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
import FormsFieldChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
import ShopInfoComponent from "./shop-info.component";
|
import ShopInfoComponent from "./shop-info.component";
|
||||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
import { FEATURE_CONFIGS, useFormDataPreservation } from "./useFormDataPreservation";
|
||||||
|
|
||||||
export default function ShopInfoContainer() {
|
export default function ShopInfoContainer() {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
@@ -22,16 +23,24 @@ export default function ShopInfoContainer() {
|
|||||||
});
|
});
|
||||||
const notification = useNotification();
|
const notification = useNotification();
|
||||||
|
|
||||||
const handleFinish = (values) => {
|
const combinedFeatureConfig = {
|
||||||
|
...FEATURE_CONFIGS.general,
|
||||||
|
...FEATURE_CONFIGS.responsibilitycenters
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use form data preservation for all shop-info features
|
||||||
|
const { createSubmissionHandler } = useFormDataPreservation(form, data?.bodyshops[0], combinedFeatureConfig);
|
||||||
|
|
||||||
|
const handleFinish = createSubmissionHandler((values) => {
|
||||||
setSaveLoading(true);
|
setSaveLoading(true);
|
||||||
logImEXEvent("shop_update");
|
logImEXEvent("shop_update");
|
||||||
|
|
||||||
updateBodyshop({
|
updateBodyshop({
|
||||||
variables: { id: data.bodyshops[0].id, shop: values }
|
variables: { id: data.bodyshops[0].id, shop: values }
|
||||||
})
|
})
|
||||||
.then((r) => {
|
.then(() => {
|
||||||
notification["success"]({ message: t("bodyshop.successes.save") });
|
notification["success"]({ message: t("bodyshop.successes.save") });
|
||||||
refetch().then((_) => form.resetFields());
|
refetch().then(() => form.resetFields());
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
@@ -39,7 +48,7 @@ export default function ShopInfoContainer() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
setSaveLoading(false);
|
setSaveLoading(false);
|
||||||
};
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data) form.resetFields();
|
if (data) form.resetFields();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
140
client/src/components/shop-info/useFormDataPreservation.js
Normal file
140
client/src/components/shop-info/useFormDataPreservation.js
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import { HasFeatureAccess } from "./../feature-wrapper/feature-wrapper.component";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom hook to preserve form data for conditionally hidden fields based on feature access
|
||||||
|
* @param {Object} form - Ant Design form instance
|
||||||
|
* @param {Object} bodyshop - Bodyshop data for feature access checks (also contains existing database values)
|
||||||
|
* @param {Object} featureConfig - Configuration object defining which features and their associated fields to preserve
|
||||||
|
*/
|
||||||
|
export const useFormDataPreservation = (form, bodyshop, featureConfig) => {
|
||||||
|
const getNestedValue = (obj, path) => {
|
||||||
|
return path.reduce((current, key) => current?.[key], obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setNestedValue = (obj, path, value) => {
|
||||||
|
const lastKey = path[path.length - 1];
|
||||||
|
const parentPath = path.slice(0, -1);
|
||||||
|
|
||||||
|
const parent = parentPath.reduce((current, key) => {
|
||||||
|
if (!current[key]) current[key] = {};
|
||||||
|
return current[key];
|
||||||
|
}, obj);
|
||||||
|
|
||||||
|
parent[lastKey] = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const preserveHiddenFormData = () => {
|
||||||
|
const preservationData = {};
|
||||||
|
let hasDataToPreserve = false;
|
||||||
|
|
||||||
|
Object.entries(featureConfig).forEach(([featureName, fieldPaths]) => {
|
||||||
|
const hasAccess = HasFeatureAccess({ featureName, bodyshop });
|
||||||
|
|
||||||
|
if (!hasAccess) {
|
||||||
|
fieldPaths.forEach((fieldPath) => {
|
||||||
|
const currentValues = form.getFieldsValue();
|
||||||
|
let value = getNestedValue(currentValues, fieldPath);
|
||||||
|
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
value = getNestedValue(bodyshop, fieldPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value !== undefined && value !== null) {
|
||||||
|
setNestedValue(preservationData, fieldPath, value);
|
||||||
|
hasDataToPreserve = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasDataToPreserve) {
|
||||||
|
form.setFieldsValue(preservationData);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCompleteFormValues = () => {
|
||||||
|
const currentFormValues = form.getFieldsValue();
|
||||||
|
const completeValues = { ...currentFormValues };
|
||||||
|
|
||||||
|
Object.entries(featureConfig).forEach(([featureName, fieldPaths]) => {
|
||||||
|
const hasAccess = HasFeatureAccess({ featureName, bodyshop });
|
||||||
|
|
||||||
|
if (!hasAccess) {
|
||||||
|
fieldPaths.forEach((fieldPath) => {
|
||||||
|
let value = getNestedValue(currentFormValues, fieldPath);
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
value = getNestedValue(bodyshop, fieldPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value !== undefined && value !== null) {
|
||||||
|
setNestedValue(completeValues, fieldPath, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return completeValues;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createSubmissionHandler = (originalHandler) => {
|
||||||
|
return () => {
|
||||||
|
const completeValues = getCompleteFormValues();
|
||||||
|
|
||||||
|
// Call the original handler with complete values including hidden data
|
||||||
|
return originalHandler(completeValues);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
preserveHiddenFormData();
|
||||||
|
}, [bodyshop]);
|
||||||
|
|
||||||
|
return { preserveHiddenFormData, getCompleteFormValues, createSubmissionHandler };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predefined feature configurations for common shop-info components
|
||||||
|
*/
|
||||||
|
export const FEATURE_CONFIGS = {
|
||||||
|
responsibilitycenters: {
|
||||||
|
export: [
|
||||||
|
["md_responsibility_centers", "costs"],
|
||||||
|
["md_responsibility_centers", "profits"],
|
||||||
|
["md_responsibility_centers", "defaults"],
|
||||||
|
["md_responsibility_centers", "dms_defaults"],
|
||||||
|
["md_responsibility_centers", "taxes", "itemexemptcode"],
|
||||||
|
["md_responsibility_centers", "taxes", "invoiceexemptcode"],
|
||||||
|
["md_responsibility_centers", "ar"],
|
||||||
|
["md_responsibility_centers", "refund"],
|
||||||
|
["md_responsibility_centers", "sales_tax_codes"],
|
||||||
|
["md_responsibility_centers", "ttl_adjustment"],
|
||||||
|
["md_responsibility_centers", "ttl_tax_adjustment"]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
general: {
|
||||||
|
export: [
|
||||||
|
["accountingconfig", "qbo"],
|
||||||
|
["accountingconfig", "qbo_usa"],
|
||||||
|
["accountingconfig", "qbo_departmentid"],
|
||||||
|
["accountingconfig", "tiers"],
|
||||||
|
["accountingconfig", "twotierpref"],
|
||||||
|
["accountingconfig", "printlater"],
|
||||||
|
["accountingconfig", "emaillater"],
|
||||||
|
["accountingconfig", "ReceivableCustomField1"],
|
||||||
|
["accountingconfig", "ReceivableCustomField2"],
|
||||||
|
["accountingconfig", "ReceivableCustomField3"],
|
||||||
|
["md_classes"],
|
||||||
|
["enforce_class"],
|
||||||
|
["accountingconfig", "ClosingPeriod"],
|
||||||
|
["accountingconfig", "companyCode"],
|
||||||
|
["accountingconfig", "batchID"]
|
||||||
|
],
|
||||||
|
bills: [
|
||||||
|
["bill_tax_rates", "federal_tax_rate"],
|
||||||
|
["bill_tax_rates", "state_tax_rate"],
|
||||||
|
["bill_tax_rates", "local_tax_rate"]
|
||||||
|
],
|
||||||
|
timetickets: [["tt_allow_post_to_invoiced"], ["tt_enforce_hours_for_tech_console"], ["bill_allow_post_to_closed"]]
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -52,6 +52,7 @@ const VendorSearchSelect = ({ value, onChange, options, onSelect, disabled, pref
|
|||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{discount && discount !== 0 ? <Tag color="green">{`${discount * 100}%`}</Tag> : null}
|
{discount && discount !== 0 ? <Tag color="green">{`${discount * 100}%`}</Tag> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -116,6 +117,11 @@ const VendorSearchSelect = ({ value, onChange, options, onSelect, disabled, pref
|
|||||||
{o.name}
|
{o.name}
|
||||||
</div>
|
</div>
|
||||||
<Space style={{ marginLeft: "1rem" }}>
|
<Space style={{ marginLeft: "1rem" }}>
|
||||||
|
{o.tags?.map((tag, idx) => (
|
||||||
|
<Tag key={idx} style={{ marginLeft: "0.5rem" }}>
|
||||||
|
{tag}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
{o.phone && showPhone && <PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>}
|
{o.phone && showPhone && <PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>}
|
||||||
{o.discount && o.discount !== 0 ? <Tag color="green">{`${o.discount * 100}%`}</Tag> : null}
|
{o.discount && o.discount !== 0 ? <Tag color="green">{`${o.discount * 100}%`}</Tag> : null}
|
||||||
</Space>
|
</Space>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { DeleteFilled } from "@ant-design/icons";
|
import { DeleteFilled } from "@ant-design/icons";
|
||||||
import { useApolloClient } from "@apollo/client";
|
import { useApolloClient } from "@apollo/client";
|
||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Button, Divider, Form, Input, InputNumber, Space, Switch } from "antd";
|
import { Button, Divider, Form, Input, InputNumber, Select, Space, Switch } from "antd";
|
||||||
import { PageHeader } from "@ant-design/pro-layout";
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -179,6 +179,18 @@ export function VendorsFormComponent({
|
|||||||
}
|
}
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="tags"
|
||||||
|
label={t("vendor.fields.tags")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
type: "array"
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select mode="tags" />
|
||||||
|
</Form.Item>
|
||||||
{DmsAp.treatment === "on" && (
|
{DmsAp.treatment === "on" && (
|
||||||
<Form.Item label={t("vendors.fields.dmsid")} name="dmsid">
|
<Form.Item label={t("vendors.fields.dmsid")} name="dmsid">
|
||||||
<Input />
|
<Input />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { SyncOutlined } from "@ant-design/icons";
|
import { SyncOutlined } from "@ant-design/icons";
|
||||||
import { Button, Card, Input, Space, Table } from "antd";
|
import { Button, Card, Input, Space, Table, Tag } from "antd";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -38,6 +38,18 @@ export default function VendorsListComponent({ handleNewVendor, loading, handleO
|
|||||||
title: t("vendors.fields.city"),
|
title: t("vendors.fields.city"),
|
||||||
dataIndex: "city",
|
dataIndex: "city",
|
||||||
key: "city"
|
key: "city"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("vendors.fields.tags"),
|
||||||
|
dataIndex: "tags",
|
||||||
|
key: "tags",
|
||||||
|
render: (text, record) => (
|
||||||
|
<Space>
|
||||||
|
{record?.tags?.map((tag, idx) => (
|
||||||
|
<Tag key={idx}>{tag}</Tag>
|
||||||
|
))}
|
||||||
|
</Space>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => {
|
|||||||
const socketRef = useRef(null);
|
const socketRef = useRef(null);
|
||||||
const [clientId, setClientId] = useState(null);
|
const [clientId, setClientId] = useState(null);
|
||||||
const [isConnected, setIsConnected] = useState(false);
|
const [isConnected, setIsConnected] = useState(false);
|
||||||
const [socketInitialized, setSocketInitialized] = useState(false);
|
|
||||||
const notification = useNotification();
|
const notification = useNotification();
|
||||||
const userAssociationId = bodyshop?.associations?.[0]?.id;
|
const userAssociationId = bodyshop?.associations?.[0]?.id;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -148,13 +147,6 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => {
|
|||||||
onError: (err) => console.error("MARK_ALL_NOTIFICATIONS_READ error:", err)
|
onError: (err) => console.error("MARK_ALL_NOTIFICATIONS_READ error:", err)
|
||||||
});
|
});
|
||||||
|
|
||||||
const checkAndReconnect = () => {
|
|
||||||
if (socketRef.current && !socketRef.current.connected) {
|
|
||||||
console.log("Attempting manual reconnect due to event trigger");
|
|
||||||
socketRef.current.connect();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initializeSocket = async (token) => {
|
const initializeSocket = async (token) => {
|
||||||
if (!bodyshop || !bodyshop.id || socketRef.current) return;
|
if (!bodyshop || !bodyshop.id || socketRef.current) return;
|
||||||
@@ -166,14 +158,13 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => {
|
|||||||
auth: { token, bodyshopId: bodyshop.id },
|
auth: { token, bodyshopId: bodyshop.id },
|
||||||
reconnectionAttempts: Infinity,
|
reconnectionAttempts: Infinity,
|
||||||
reconnectionDelay: 2000,
|
reconnectionDelay: 2000,
|
||||||
reconnectionDelayMax: 60000,
|
reconnectionDelayMax: 60000
|
||||||
randomizationFactor: 0.5,
|
// randomizationFactor: 0.5,
|
||||||
transports: ["websocket", "polling"], // Add this to prefer WebSocket with polling fallback
|
// transports: ["websocket", "polling"], // Add this to prefer WebSocket with polling fallback
|
||||||
rememberUpgrade: true
|
// rememberUpgrade: true
|
||||||
});
|
});
|
||||||
|
|
||||||
socketRef.current = socketInstance;
|
socketRef.current = socketInstance;
|
||||||
setSocketInitialized(true);
|
|
||||||
|
|
||||||
const handleBodyshopMessage = (message) => {
|
const handleBodyshopMessage = (message) => {
|
||||||
if (!message || !message.type) return;
|
if (!message || !message.type) return;
|
||||||
@@ -261,7 +252,7 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConnect = () => {
|
const handleConnect = () => {
|
||||||
socketInstance.emit("join-bodyshop-room", bodyshop.id);
|
socketInstance.emit("join-bodyshop-room", bodyshop.id);
|
||||||
setClientId(socketInstance.id);
|
setClientId(socketInstance.id);
|
||||||
@@ -558,57 +549,6 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => {
|
|||||||
t
|
t
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!socketInitialized) return;
|
|
||||||
|
|
||||||
const onVisibilityChange = () => {
|
|
||||||
if (document.visibilityState === "visible") {
|
|
||||||
checkAndReconnect();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onFocus = () => {
|
|
||||||
checkAndReconnect();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onOnline = () => {
|
|
||||||
checkAndReconnect();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPageShow = (event) => {
|
|
||||||
if (event.persisted) {
|
|
||||||
checkAndReconnect();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
||||||
window.addEventListener("focus", onFocus);
|
|
||||||
window.addEventListener("online", onOnline);
|
|
||||||
window.addEventListener("pageshow", onPageShow);
|
|
||||||
|
|
||||||
// Sleep/wake detection using timer
|
|
||||||
let lastTime = Date.now();
|
|
||||||
const intervalMs = 1000; // Check every second
|
|
||||||
const thresholdMs = 2000; // If more than 2 seconds elapsed, assume sleep/wake
|
|
||||||
|
|
||||||
const sleepCheckInterval = setInterval(() => {
|
|
||||||
const currentTime = Date.now();
|
|
||||||
if (currentTime > lastTime + intervalMs + thresholdMs) {
|
|
||||||
console.log("Detected potential wake from sleep/hibernate");
|
|
||||||
checkAndReconnect();
|
|
||||||
}
|
|
||||||
lastTime = currentTime;
|
|
||||||
}, intervalMs);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
||||||
window.removeEventListener("focus", onFocus);
|
|
||||||
window.removeEventListener("online", onOnline);
|
|
||||||
window.removeEventListener("pageshow", onPageShow);
|
|
||||||
clearInterval(sleepCheckInterval);
|
|
||||||
};
|
|
||||||
}, [socketInitialized]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SocketContext.Provider
|
<SocketContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export const QUERY_VENDOR_BY_ID = gql`
|
|||||||
active
|
active
|
||||||
phone
|
phone
|
||||||
dmsid
|
dmsid
|
||||||
|
tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -54,6 +55,7 @@ export const QUERY_ALL_VENDORS = gql`
|
|||||||
city
|
city
|
||||||
phone
|
phone
|
||||||
active
|
active
|
||||||
|
tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -89,6 +91,7 @@ export const QUERY_ALL_VENDORS_FOR_ORDER = gql`
|
|||||||
email
|
email
|
||||||
active
|
active
|
||||||
phone
|
phone
|
||||||
|
tags
|
||||||
}
|
}
|
||||||
jobs(where: { id: { _eq: $jobId } }) {
|
jobs(where: { id: { _eq: $jobId } }) {
|
||||||
v_make_desc
|
v_make_desc
|
||||||
@@ -105,6 +108,7 @@ export const SEARCH_VENDOR_AUTOCOMPLETE = gql`
|
|||||||
cost_center
|
cost_center
|
||||||
active
|
active
|
||||||
favorite
|
favorite
|
||||||
|
tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -124,6 +128,7 @@ export const SEARCH_VENDOR_AUTOCOMPLETE_WITH_ADDR = gql`
|
|||||||
email
|
email
|
||||||
state
|
state
|
||||||
active
|
active
|
||||||
|
tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { EditFilled, SyncOutlined } from "@ant-design/icons";
|
|||||||
import { Button, Card, Checkbox, Input, Space, Table, Typography } from "antd";
|
import { Button, Card, Checkbox, Input, Space, Table, Typography } from "antd";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||||
@@ -13,8 +13,9 @@ import { setModalContext } from "../../redux/modals/modals.actions";
|
|||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
|
||||||
import { pageLimit } from "../../utils/config";
|
import { pageLimit } from "../../utils/config";
|
||||||
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
|
import useLocalStorage from "../../utils/useLocalStorage";
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setPartsOrderContext: (context) => dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
setPartsOrderContext: (context) => dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
||||||
@@ -27,7 +28,7 @@ export function BillsListPage({ loading, data, refetch, total, setPartsOrderCont
|
|||||||
const [searchLoading, setSearchLoading] = useState(false);
|
const [searchLoading, setSearchLoading] = useState(false);
|
||||||
const { page } = search;
|
const { page } = search;
|
||||||
const history = useNavigate();
|
const history = useNavigate();
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useLocalStorage("bills_list_sort", {
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
filteredInfo: { text: "" }
|
filteredInfo: { text: "" }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, set
|
|||||||
{
|
{
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
({ getFieldValue }) => ({
|
() => ({
|
||||||
validator(_, value) {
|
validator(_, value) {
|
||||||
if (!bodyshop.cdk_dealerid) return Promise.resolve();
|
if (!bodyshop.cdk_dealerid) return Promise.resolve();
|
||||||
if (!value || dayjs(value).isSameOrAfter(dayjs(), "day")) {
|
if (!value || dayjs(value).isSameOrAfter(dayjs(), "day")) {
|
||||||
@@ -280,7 +280,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, set
|
|||||||
return Promise.reject(new Error(t("jobs.labels.dms.invoicedatefuture")));
|
return Promise.reject(new Error(t("jobs.labels.dms.invoicedatefuture")));
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
({ getFieldValue }) => ({
|
() => ({
|
||||||
validator(_, value) {
|
validator(_, value) {
|
||||||
if (ClosingPeriod.treatment === "on" && bodyshop.accountingconfig.ClosingPeriod) {
|
if (ClosingPeriod.treatment === "on" && bodyshop.accountingconfig.ClosingPeriod) {
|
||||||
if (
|
if (
|
||||||
@@ -369,8 +369,8 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, set
|
|||||||
<Form.List
|
<Form.List
|
||||||
name={["qb_multiple_payers"]}
|
name={["qb_multiple_payers"]}
|
||||||
rules={[
|
rules={[
|
||||||
({ getFieldValue }) => ({
|
() => ({
|
||||||
validator(_, value) {
|
validator() {
|
||||||
let totalAllocated = Dinero();
|
let totalAllocated = Dinero();
|
||||||
|
|
||||||
const payers = form.getFieldValue("qb_multiple_payers");
|
const payers = form.getFieldValue("qb_multiple_payers");
|
||||||
@@ -492,7 +492,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, set
|
|||||||
<Statistic
|
<Statistic
|
||||||
title={t("jobs.labels.pimraryamountpayable")}
|
title={t("jobs.labels.pimraryamountpayable")}
|
||||||
valueStyle={{
|
valueStyle={{
|
||||||
color: discrep.getAmount() > 0 ? "green" : "red"
|
color: discrep.getAmount() >= 0 ? "green" : "red"
|
||||||
}}
|
}}
|
||||||
value={discrep.toFormat()}
|
value={discrep.toFormat()}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -517,6 +517,7 @@
|
|||||||
"dashboard": "Shop -> Dashboard",
|
"dashboard": "Shop -> Dashboard",
|
||||||
"rbac": "Shop -> RBAC",
|
"rbac": "Shop -> RBAC",
|
||||||
"reportcenter": "Shop -> Report Center",
|
"reportcenter": "Shop -> Report Center",
|
||||||
|
"responsibilitycenter": "Shop -> Responsibility Centers",
|
||||||
"templates": "Shop -> Templates",
|
"templates": "Shop -> Templates",
|
||||||
"vendors": "Shop -> Vendors"
|
"vendors": "Shop -> Vendors"
|
||||||
},
|
},
|
||||||
@@ -3890,6 +3891,7 @@
|
|||||||
"state": "Province/State",
|
"state": "Province/State",
|
||||||
"street1": "Street",
|
"street1": "Street",
|
||||||
"street2": "Address 2",
|
"street2": "Address 2",
|
||||||
|
"tags": "Tags",
|
||||||
"taxid": "Tax ID",
|
"taxid": "Tax ID",
|
||||||
"terms": "Payment Terms",
|
"terms": "Payment Terms",
|
||||||
"zip": "Zip/Postal Code"
|
"zip": "Zip/Postal Code"
|
||||||
|
|||||||
@@ -517,6 +517,7 @@
|
|||||||
"dashboard": "",
|
"dashboard": "",
|
||||||
"rbac": "",
|
"rbac": "",
|
||||||
"reportcenter": "",
|
"reportcenter": "",
|
||||||
|
"responsibilitycenter": "",
|
||||||
"templates": "",
|
"templates": "",
|
||||||
"vendors": ""
|
"vendors": ""
|
||||||
},
|
},
|
||||||
@@ -3890,6 +3891,7 @@
|
|||||||
"state": "Provincia del estado",
|
"state": "Provincia del estado",
|
||||||
"street1": "calle",
|
"street1": "calle",
|
||||||
"street2": "Dirección 2",
|
"street2": "Dirección 2",
|
||||||
|
"tags": "",
|
||||||
"taxid": "Identificación del impuesto",
|
"taxid": "Identificación del impuesto",
|
||||||
"terms": "Términos de pago",
|
"terms": "Términos de pago",
|
||||||
"zip": "código postal"
|
"zip": "código postal"
|
||||||
|
|||||||
@@ -517,6 +517,7 @@
|
|||||||
"dashboard": "",
|
"dashboard": "",
|
||||||
"rbac": "",
|
"rbac": "",
|
||||||
"reportcenter": "",
|
"reportcenter": "",
|
||||||
|
"responsibilitycenter": "",
|
||||||
"templates": "",
|
"templates": "",
|
||||||
"vendors": ""
|
"vendors": ""
|
||||||
},
|
},
|
||||||
@@ -3890,6 +3891,7 @@
|
|||||||
"state": "Etat / Province",
|
"state": "Etat / Province",
|
||||||
"street1": "rue",
|
"street1": "rue",
|
||||||
"street2": "Adresse 2 ",
|
"street2": "Adresse 2 ",
|
||||||
|
"tags": "",
|
||||||
"taxid": "Identifiant de taxe",
|
"taxid": "Identifiant de taxe",
|
||||||
"terms": "Modalités de paiement",
|
"terms": "Modalités de paiement",
|
||||||
"zip": "Zip / code postal"
|
"zip": "Zip / code postal"
|
||||||
|
|||||||
@@ -48,24 +48,24 @@ export default async function RenderTemplate(
|
|||||||
...(renderAsHtml
|
...(renderAsHtml
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
recipe: "chrome-pdf",
|
recipe: "chrome-pdf",
|
||||||
...(!ignoreCustomMargins && {
|
...(!ignoreCustomMargins && {
|
||||||
chrome: {
|
chrome: {
|
||||||
marginTop:
|
marginTop:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.headerMargin &&
|
bodyshop.logo_img_path.headerMargin &&
|
||||||
bodyshop.logo_img_path.headerMargin > 36
|
bodyshop.logo_img_path.headerMargin > 36
|
||||||
? bodyshop.logo_img_path.headerMargin
|
? bodyshop.logo_img_path.headerMargin
|
||||||
: "36px",
|
: "36px",
|
||||||
marginBottom:
|
marginBottom:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.footerMargin &&
|
bodyshop.logo_img_path.footerMargin &&
|
||||||
bodyshop.logo_img_path.footerMargin > 50
|
bodyshop.logo_img_path.footerMargin > 50
|
||||||
? bodyshop.logo_img_path.footerMargin
|
? bodyshop.logo_img_path.footerMargin
|
||||||
: "50px"
|
: "50px"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
...(renderAsExcel ? { recipe: "html-to-xlsx" } : {}),
|
...(renderAsExcel ? { recipe: "html-to-xlsx" } : {}),
|
||||||
...(renderAsText ? { recipe: "text" } : {})
|
...(renderAsText ? { recipe: "text" } : {})
|
||||||
},
|
},
|
||||||
@@ -100,14 +100,14 @@ export default async function RenderTemplate(
|
|||||||
chrome: {
|
chrome: {
|
||||||
marginTop:
|
marginTop:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.headerMargin &&
|
bodyshop.logo_img_path.headerMargin &&
|
||||||
bodyshop.logo_img_path.headerMargin > 36
|
bodyshop.logo_img_path.headerMargin > 36
|
||||||
? bodyshop.logo_img_path.headerMargin
|
? bodyshop.logo_img_path.headerMargin
|
||||||
: "36px",
|
: "36px",
|
||||||
marginBottom:
|
marginBottom:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.footerMargin &&
|
bodyshop.logo_img_path.footerMargin &&
|
||||||
bodyshop.logo_img_path.footerMargin > 50
|
bodyshop.logo_img_path.footerMargin > 50
|
||||||
? bodyshop.logo_img_path.footerMargin
|
? bodyshop.logo_img_path.footerMargin
|
||||||
: "50px"
|
: "50px"
|
||||||
}
|
}
|
||||||
@@ -182,22 +182,22 @@ export async function RenderTemplates(templateObjects, bodyshop, renderAsHtml =
|
|||||||
...(renderAsHtml
|
...(renderAsHtml
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
recipe: "chrome-pdf",
|
recipe: "chrome-pdf",
|
||||||
chrome: {
|
chrome: {
|
||||||
marginTop:
|
marginTop:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.headerMargin &&
|
bodyshop.logo_img_path.headerMargin &&
|
||||||
bodyshop.logo_img_path.headerMargin > 36
|
bodyshop.logo_img_path.headerMargin > 36
|
||||||
? bodyshop.logo_img_path.headerMargin
|
? bodyshop.logo_img_path.headerMargin
|
||||||
: "36px",
|
: "36px",
|
||||||
marginBottom:
|
marginBottom:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.footerMargin &&
|
bodyshop.logo_img_path.footerMargin &&
|
||||||
bodyshop.logo_img_path.footerMargin > 50
|
bodyshop.logo_img_path.footerMargin > 50
|
||||||
? bodyshop.logo_img_path.footerMargin
|
? bodyshop.logo_img_path.footerMargin
|
||||||
: "50px"
|
: "50px"
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
pdfOperations: [
|
pdfOperations: [
|
||||||
{
|
{
|
||||||
template: {
|
template: {
|
||||||
@@ -213,14 +213,14 @@ export async function RenderTemplates(templateObjects, bodyshop, renderAsHtml =
|
|||||||
chrome: {
|
chrome: {
|
||||||
marginTop:
|
marginTop:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.headerMargin &&
|
bodyshop.logo_img_path.headerMargin &&
|
||||||
bodyshop.logo_img_path.headerMargin > 36
|
bodyshop.logo_img_path.headerMargin > 36
|
||||||
? bodyshop.logo_img_path.headerMargin
|
? bodyshop.logo_img_path.headerMargin
|
||||||
: "36px",
|
: "36px",
|
||||||
marginBottom:
|
marginBottom:
|
||||||
bodyshop.logo_img_path &&
|
bodyshop.logo_img_path &&
|
||||||
bodyshop.logo_img_path.footerMargin &&
|
bodyshop.logo_img_path.footerMargin &&
|
||||||
bodyshop.logo_img_path.footerMargin > 50
|
bodyshop.logo_img_path.footerMargin > 50
|
||||||
? bodyshop.logo_img_path.footerMargin
|
? bodyshop.logo_img_path.footerMargin
|
||||||
: "50px"
|
: "50px"
|
||||||
},
|
},
|
||||||
@@ -302,7 +302,6 @@ export const fetchFilterData = async ({ name }) => {
|
|||||||
const jsReportFilters = await cleanAxios.get(`${server}/odata/assets?$filter=name eq '${name}.filters'`, {
|
const jsReportFilters = await cleanAxios.get(`${server}/odata/assets?$filter=name eq '${name}.filters'`, {
|
||||||
headers: { Authorization: jsrAuth }
|
headers: { Authorization: jsrAuth }
|
||||||
});
|
});
|
||||||
console.log("🚀 ~ fetchFilterData ~ jsReportFilters:", jsReportFilters);
|
|
||||||
|
|
||||||
let parsedFilterData;
|
let parsedFilterData;
|
||||||
let useShopSpecificTemplate = false;
|
let useShopSpecificTemplate = false;
|
||||||
|
|||||||
@@ -7123,6 +7123,7 @@
|
|||||||
- state
|
- state
|
||||||
- street1
|
- street1
|
||||||
- street2
|
- street2
|
||||||
|
- tags
|
||||||
- updated_at
|
- updated_at
|
||||||
- zip
|
- zip
|
||||||
select_permissions:
|
select_permissions:
|
||||||
@@ -7146,6 +7147,7 @@
|
|||||||
- state
|
- state
|
||||||
- street1
|
- street1
|
||||||
- street2
|
- street2
|
||||||
|
- tags
|
||||||
- updated_at
|
- updated_at
|
||||||
- zip
|
- zip
|
||||||
filter:
|
filter:
|
||||||
@@ -7179,6 +7181,7 @@
|
|||||||
- state
|
- state
|
||||||
- street1
|
- street1
|
||||||
- street2
|
- street2
|
||||||
|
- tags
|
||||||
- updated_at
|
- updated_at
|
||||||
- zip
|
- zip
|
||||||
filter:
|
filter:
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."vendors" add column "tags" jsonb
|
||||||
|
-- not null default jsonb_build_array();
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."vendors" add column "tags" jsonb
|
||||||
|
not null default jsonb_build_array();
|
||||||
@@ -144,6 +144,7 @@ const paymentRefund = async (req, res) => {
|
|||||||
|
|
||||||
logger.log("intellipay-refund-success", "DEBUG", req.user?.email, null, {
|
logger.log("intellipay-refund-success", "DEBUG", req.user?.email, null, {
|
||||||
requestOptions: options,
|
requestOptions: options,
|
||||||
|
response: response?.data,
|
||||||
...logResponseMeta
|
...logResponseMeta
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user