@@ -4535,6 +4535,48 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>localmediaserverhttp</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>
|
||||||
|
<name>localmediaservernetwork</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>logo_img_footer_margin</name>
|
<name>logo_img_footer_margin</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -8261,6 +8303,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>uselocalmediaserver</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>website</name>
|
<name>website</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -13253,6 +13316,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>openinexplorer</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>reassign_limitexceeded</name>
|
<name>reassign_limitexceeded</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
"markerjs2": "^2.21.1",
|
"markerjs2": "^2.21.1",
|
||||||
"moment-business-days": "^1.2.0",
|
"moment-business-days": "^1.2.0",
|
||||||
"moment-timezone": "^0.5.34",
|
"moment-timezone": "^0.5.34",
|
||||||
|
"normalize-url": "^7.0.3",
|
||||||
"phone": "^3.1.15",
|
"phone": "^3.1.15",
|
||||||
"preval.macro": "^5.0.0",
|
"preval.macro": "^5.0.0",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
|
|||||||
@@ -12,27 +12,29 @@ import moment from "moment";
|
|||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useLocation, useHistory } from "react-router-dom";
|
import { connect } from "react-redux";
|
||||||
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
import {
|
import {
|
||||||
DELETE_BILL_LINE,
|
DELETE_BILL_LINE,
|
||||||
INSERT_NEW_BILL_LINES,
|
INSERT_NEW_BILL_LINES,
|
||||||
UPDATE_BILL_LINE,
|
UPDATE_BILL_LINE,
|
||||||
} from "../../graphql/bill-lines.queries";
|
} from "../../graphql/bill-lines.queries";
|
||||||
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
|
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
|
||||||
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import BillFormContainer from "../bill-form/bill-form.container";
|
import BillFormContainer from "../bill-form/bill-form.container";
|
||||||
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
|
|
||||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
|
||||||
import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { createStructuredSelector } from "reselect";
|
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
|
||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
|
||||||
import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component";
|
import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component";
|
||||||
|
import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component";
|
||||||
|
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
|
||||||
|
import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
||||||
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setPartsOrderContext: (context) =>
|
setPartsOrderContext: (context) =>
|
||||||
@@ -49,6 +51,7 @@ export default connect(
|
|||||||
export function BillDetailEditcontainer({
|
export function BillDetailEditcontainer({
|
||||||
setPartsOrderContext,
|
setPartsOrderContext,
|
||||||
insertAuditTrail,
|
insertAuditTrail,
|
||||||
|
bodyshop,
|
||||||
}) {
|
}) {
|
||||||
const search = queryString.parse(useLocation().search);
|
const search = queryString.parse(useLocation().search);
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
@@ -265,12 +268,21 @@ export function BillDetailEditcontainer({
|
|||||||
layout="vertical"
|
layout="vertical"
|
||||||
>
|
>
|
||||||
<BillFormContainer form={form} billEdit disabled={exported} />
|
<BillFormContainer form={form} billEdit disabled={exported} />
|
||||||
<JobDocumentsGallery
|
|
||||||
jobId={data ? data.bills_by_pk.jobid : null}
|
{bodyshop.uselocalmediaserver ? (
|
||||||
billId={search.billid}
|
<JobsDocumentsLocalGallery
|
||||||
documentsList={data ? data.bills_by_pk.documents : []}
|
job={{ id: data ? data.bills_by_pk.jobid : null }}
|
||||||
billsCallback={refetch}
|
invoice_number={data ? data.bills_by_pk.invoice_number : null}
|
||||||
/>
|
vendorid={data ? data.bills_by_pk.vendorid : null}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<JobDocumentsGallery
|
||||||
|
jobId={data ? data.bills_by_pk.jobid : null}
|
||||||
|
billId={search.billid}
|
||||||
|
documentsList={data ? data.bills_by_pk.documents : []}
|
||||||
|
billsCallback={refetch}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
|||||||
import BillFormContainer from "../bill-form/bill-form.container";
|
import BillFormContainer from "../bill-form/bill-form.container";
|
||||||
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
|
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
|
||||||
import { handleUpload } from "../documents-upload/documents-upload.utility";
|
import { handleUpload } from "../documents-upload/documents-upload.utility";
|
||||||
|
import { handleUpload as handleLocalUpload } from "../documents-local-upload/documents-local-upload.utility";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
billEnterModal: selectBillEnterModal,
|
billEnterModal: selectBillEnterModal,
|
||||||
@@ -210,19 +211,33 @@ function BillEnterModalContainer({
|
|||||||
/////////////////////////
|
/////////////////////////
|
||||||
if (upload && upload.length > 0) {
|
if (upload && upload.length > 0) {
|
||||||
//insert Each of the documents?
|
//insert Each of the documents?
|
||||||
upload.forEach((u) => {
|
|
||||||
handleUpload(
|
if (bodyshop.uselocalmediaserver) {
|
||||||
{ file: u.originFileObj },
|
upload.forEach((u) => {
|
||||||
{
|
handleLocalUpload({
|
||||||
bodyshop: bodyshop,
|
ev: { file: u.originFileObj },
|
||||||
uploaded_by: currentUser.email,
|
context: {
|
||||||
jobId: values.jobid,
|
jobid: values.jobid,
|
||||||
billId: billId,
|
invoice_number: remainingValues.invoice_number,
|
||||||
tagsArray: null,
|
vendorid: remainingValues.vendorid,
|
||||||
callback: null,
|
},
|
||||||
}
|
});
|
||||||
);
|
});
|
||||||
});
|
} else {
|
||||||
|
upload.forEach((u) => {
|
||||||
|
handleUpload(
|
||||||
|
{ file: u.originFileObj },
|
||||||
|
{
|
||||||
|
bodyshop: bodyshop,
|
||||||
|
uploaded_by: currentUser.email,
|
||||||
|
jobId: values.jobid,
|
||||||
|
billId: billId,
|
||||||
|
tagsArray: null,
|
||||||
|
callback: null,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
///////////////////////////
|
///////////////////////////
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries";
|
import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import JobDocumentsGalleryExternal from "../jobs-documents-gallery/jobs-documents-gallery.external.component";
|
import JobDocumentsGalleryExternal from "../jobs-documents-gallery/jobs-documents-gallery.external.component";
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
@@ -19,6 +20,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
export default connect(mapStateToProps, mapDispatchToProps)(ChatMediaSelector);
|
export default connect(mapStateToProps, mapDispatchToProps)(ChatMediaSelector);
|
||||||
|
|
||||||
export function ChatMediaSelector({
|
export function ChatMediaSelector({
|
||||||
|
bodyshop,
|
||||||
selectedMedia,
|
selectedMedia,
|
||||||
setSelectedMedia,
|
setSelectedMedia,
|
||||||
conversation,
|
conversation,
|
||||||
@@ -27,7 +29,6 @@ export function ChatMediaSelector({
|
|||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
|
|
||||||
const { loading, error, data } = useQuery(GET_DOCUMENTS_BY_JOB, {
|
const { loading, error, data } = useQuery(GET_DOCUMENTS_BY_JOB, {
|
||||||
|
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
variables: {
|
variables: {
|
||||||
@@ -66,6 +67,8 @@ export function ChatMediaSelector({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (bodyshop.uselocalmediaserver) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover
|
<Popover
|
||||||
content={
|
content={
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export function ChatPresetsComponent({ bodyshop, setMessage, className }) {
|
|||||||
const menu = (
|
const menu = (
|
||||||
<Menu>
|
<Menu>
|
||||||
{bodyshop.md_messaging_presets.map((i, idx) => (
|
{bodyshop.md_messaging_presets.map((i, idx) => (
|
||||||
<Menu.Item onClick={() => setMessage(i.text)} onItemHover key={idx}>
|
<Menu.Item onClick={() => setMessage(i.text)} key={idx}>
|
||||||
{i.label}
|
{i.label}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import { UploadOutlined } from "@ant-design/icons";
|
||||||
|
import { Upload } from "antd";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import {
|
||||||
|
selectBodyshop,
|
||||||
|
selectCurrentUser,
|
||||||
|
} from "../../redux/user/user.selectors";
|
||||||
|
import { handleUpload } from "./documents-local-upload.utility";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function DocumentsLocalUploadComponent({
|
||||||
|
children,
|
||||||
|
currentUser,
|
||||||
|
bodyshop,
|
||||||
|
job,
|
||||||
|
vendorid,
|
||||||
|
invoice_number,
|
||||||
|
callbackAfterUpload,
|
||||||
|
}) {
|
||||||
|
const [fileList, setFileList] = useState([]);
|
||||||
|
|
||||||
|
const handleDone = (uid) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
setFileList((fileList) => fileList.filter((x) => x.uid !== uid));
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Upload.Dragger
|
||||||
|
multiple={true}
|
||||||
|
fileList={fileList}
|
||||||
|
onChange={(f) => {
|
||||||
|
if (f.event && f.event.percent === 100) handleDone(f.file.uid);
|
||||||
|
|
||||||
|
setFileList(f.fileList);
|
||||||
|
}}
|
||||||
|
customRequest={(ev) =>
|
||||||
|
handleUpload({
|
||||||
|
ev,
|
||||||
|
context: {
|
||||||
|
jobid: job.id,
|
||||||
|
vendorid,
|
||||||
|
invoice_number,
|
||||||
|
callback: callbackAfterUpload,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
accept="audio/*, video/*, image/*, .pdf, .doc, .docx, .xls, .xlsx"
|
||||||
|
>
|
||||||
|
{children || (
|
||||||
|
<>
|
||||||
|
<p className="ant-upload-drag-icon">
|
||||||
|
<UploadOutlined />
|
||||||
|
</p>
|
||||||
|
<p className="ant-upload-text">
|
||||||
|
Click or drag files to this area to upload.
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Upload.Dragger>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default connect(mapStateToProps, null)(DocumentsLocalUploadComponent);
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import cleanAxios from "../../utils/CleanAxios";
|
||||||
|
import { store } from "../../redux/store";
|
||||||
|
import { addMediaForJob } from "../../redux/media/media.actions";
|
||||||
|
import normalizeUrl from "normalize-url";
|
||||||
|
|
||||||
|
export const handleUpload = async ({ ev, context }) => {
|
||||||
|
const { onError, onSuccess, onProgress, file } = ev;
|
||||||
|
const { jobid, invoice_number, vendorid, callbackAfterUpload } = context;
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
headers: { "X-Requested-With": "XMLHttpRequest" },
|
||||||
|
onUploadProgress: (e) => {
|
||||||
|
if (!!onProgress) onProgress({ percent: (e.loaded / e.total) * 100 });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
formData.append("jobid", jobid);
|
||||||
|
if (invoice_number) {
|
||||||
|
formData.append("invoice_number", invoice_number);
|
||||||
|
formData.append("vendorid", vendorid);
|
||||||
|
}
|
||||||
|
formData.append("file", file);
|
||||||
|
const bodyshop = store.getState().user.bodyshop;
|
||||||
|
|
||||||
|
const imexMediaServerResponse = await cleanAxios.post(
|
||||||
|
normalizeUrl(
|
||||||
|
`${bodyshop.localmediaserverhttp}/${
|
||||||
|
invoice_number ? "bills" : "jobs"
|
||||||
|
}/upload`
|
||||||
|
),
|
||||||
|
formData,
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (imexMediaServerResponse.status !== 200) {
|
||||||
|
if (!!onError) {
|
||||||
|
onError(imexMediaServerResponse.statusText);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onSuccess && onSuccess(file);
|
||||||
|
store.dispatch(
|
||||||
|
addMediaForJob({
|
||||||
|
jobid,
|
||||||
|
media: imexMediaServerResponse.data.map((d) => {
|
||||||
|
return {
|
||||||
|
...d,
|
||||||
|
selected: false,
|
||||||
|
src: normalizeUrl(`${bodyshop.localmediaserverhttp}/${d.src}`),
|
||||||
|
thumbnail: normalizeUrl(
|
||||||
|
`${bodyshop.localmediaserverhttp}/${d.thumbnail}`
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callbackAfterUpload) {
|
||||||
|
callbackAfterUpload();
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
Space,
|
Space,
|
||||||
Menu,
|
Menu,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
|
Button,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -20,10 +21,13 @@ import {
|
|||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectCurrentUser,
|
selectCurrentUser,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
|
import { CreateExplorerLinkForJob } from "../../utils/localmedia";
|
||||||
|
import { selectEmailConfig } from "../../redux/email/email.selectors";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
|
emailConfig: selectEmailConfig,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
@@ -34,6 +38,7 @@ export default connect(
|
|||||||
)(EmailOverlayComponent);
|
)(EmailOverlayComponent);
|
||||||
|
|
||||||
export function EmailOverlayComponent({
|
export function EmailOverlayComponent({
|
||||||
|
emailConfig,
|
||||||
form,
|
form,
|
||||||
selectedMediaState,
|
selectedMediaState,
|
||||||
bodyshop,
|
bodyshop,
|
||||||
@@ -153,10 +158,17 @@ export function EmailOverlayComponent({
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tabs.TabPane tab={t("emails.labels.documents")} key="documents">
|
{!bodyshop.uselocalmediaserver && (
|
||||||
<EmailDocumentsComponent selectedMediaState={selectedMediaState} />
|
<Tabs.TabPane tab={t("emails.labels.documents")} key="documents">
|
||||||
</Tabs.TabPane>
|
<EmailDocumentsComponent selectedMediaState={selectedMediaState} />
|
||||||
|
</Tabs.TabPane>
|
||||||
|
)}
|
||||||
<Tabs.TabPane tab={t("emails.labels.attachments")} key="attachments">
|
<Tabs.TabPane tab={t("emails.labels.attachments")} key="attachments">
|
||||||
|
{bodyshop.uselocalmediaserver && emailConfig.jobid && (
|
||||||
|
<a href={CreateExplorerLinkForJob({ jobid: emailConfig.jobid })}>
|
||||||
|
<Button>{t("documents.labels.openinexplorer")}</Button>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="fileList"
|
name="fileList"
|
||||||
valuePropName="fileList"
|
valuePropName="fileList"
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ 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 { Link, useHistory, useLocation } from "react-router-dom";
|
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
import { QUERY_JOB_CARD_DETAILS } from "../../graphql/jobs.queries";
|
import { QUERY_JOB_CARD_DETAILS } from "../../graphql/jobs.queries";
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import JobSyncButton from "../job-sync-button/job-sync-button.component";
|
import JobSyncButton from "../job-sync-button/job-sync-button.component";
|
||||||
import JobsDetailHeader from "../jobs-detail-header/jobs-detail-header.component";
|
import JobsDetailHeader from "../jobs-detail-header/jobs-detail-header.component";
|
||||||
@@ -20,6 +22,10 @@ import JobDetailCardsNotesComponent from "./job-detail-cards.notes.component";
|
|||||||
import JobDetailCardsPartsComponent from "./job-detail-cards.parts.component";
|
import JobDetailCardsPartsComponent from "./job-detail-cards.parts.component";
|
||||||
import JobDetailCardsTotalsComponent from "./job-detail-cards.totals.component";
|
import JobDetailCardsTotalsComponent from "./job-detail-cards.totals.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setPrintCenterContext: (context) =>
|
setPrintCenterContext: (context) =>
|
||||||
dispatch(setModalContext({ context: context, modal: "printCenter" })),
|
dispatch(setModalContext({ context: context, modal: "printCenter" })),
|
||||||
@@ -31,7 +37,7 @@ const span = {
|
|||||||
lg: { span: 8 },
|
lg: { span: 8 },
|
||||||
};
|
};
|
||||||
|
|
||||||
export function JobDetailCards({ setPrintCenterContext }) {
|
export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
|
||||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||||
.filter((screen) => !!screen[1])
|
.filter((screen) => !!screen[1])
|
||||||
.slice(-1)[0];
|
.slice(-1)[0];
|
||||||
@@ -143,12 +149,14 @@ export function JobDetailCards({ setPrintCenterContext }) {
|
|||||||
data={data ? data.jobs_by_pk : null}
|
data={data ? data.jobs_by_pk : null}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col {...span}>
|
{!bodyshop.uselocalmediaserver && (
|
||||||
<JobDetailCardsDocumentsComponent
|
<Col {...span}>
|
||||||
loading={loading}
|
<JobDetailCardsDocumentsComponent
|
||||||
data={data ? data.jobs_by_pk : null}
|
loading={loading}
|
||||||
/>
|
data={data ? data.jobs_by_pk : null}
|
||||||
</Col>
|
/>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
<Col {...span}>
|
<Col {...span}>
|
||||||
<JobDetailCardsDamageComponent
|
<JobDetailCardsDamageComponent
|
||||||
loading={loading}
|
loading={loading}
|
||||||
@@ -161,4 +169,4 @@ export function JobDetailCards({ setPrintCenterContext }) {
|
|||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default connect(null, mapDispatchToProps)(JobDetailCards);
|
export default connect(mapStateToProps, mapDispatchToProps)(JobDetailCards);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function JoblinePresetButton({ bodyshop, form }) {
|
|||||||
const menu = (
|
const menu = (
|
||||||
<Menu>
|
<Menu>
|
||||||
{bodyshop.md_jobline_presets.map((i, idx) => (
|
{bodyshop.md_jobline_presets.map((i, idx) => (
|
||||||
<Menu.Item onClick={() => handleSelect(i)} onItemHover key={idx}>
|
<Menu.Item onClick={() => handleSelect(i)} key={idx}>
|
||||||
{i.label}
|
{i.label}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { FileExcelFilled, EditFilled, SyncOutlined } from "@ant-design/icons";
|
import { EditFilled, FileExcelFilled, SyncOutlined } from "@ant-design/icons";
|
||||||
import { Card, Col, Row, Space, Button } from "antd";
|
import { Button, Card, Col, Row, Space } from "antd";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Gallery from "react-grid-gallery";
|
import Gallery from "react-grid-gallery";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import { SyncOutlined } from "@ant-design/icons";
|
||||||
|
import { Button, Card, Space } from "antd";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import Gallery from "react-grid-gallery";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import {
|
||||||
|
getBillMedia,
|
||||||
|
getJobMedia,
|
||||||
|
toggleMediaSelected,
|
||||||
|
} from "../../redux/media/media.actions";
|
||||||
|
import { selectAllMedia } from "../../redux/media/media.selectors";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import { CreateExplorerLinkForJob } from "../../utils/localmedia";
|
||||||
|
import DocumentsLocalUploadComponent from "../documents-local-upload/documents-local-upload.component";
|
||||||
|
import JobsDocumentsLocalGalleryReassign from "./jobs-documents-local-gallery.reassign.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
allMedia: selectAllMedia,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
getJobMedia: (id) => dispatch(getJobMedia(id)),
|
||||||
|
getBillMedia: ({ jobid, invoice_number }) => {
|
||||||
|
dispatch(getBillMedia({ jobid, invoice_number }));
|
||||||
|
},
|
||||||
|
toggleMediaSelected: ({ jobid, filename }) =>
|
||||||
|
dispatch(toggleMediaSelected({ jobid, filename })),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(JobsDocumentsLocalGallery);
|
||||||
|
|
||||||
|
export function JobsDocumentsLocalGallery({
|
||||||
|
bodyshop,
|
||||||
|
toggleMediaSelected,
|
||||||
|
getJobMedia,
|
||||||
|
getBillMedia,
|
||||||
|
allMedia,
|
||||||
|
job,
|
||||||
|
invoice_number,
|
||||||
|
vendorid,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
useEffect(() => {
|
||||||
|
if (job) {
|
||||||
|
if (invoice_number) {
|
||||||
|
getBillMedia({ jobid: job.id, invoice_number });
|
||||||
|
} else {
|
||||||
|
getJobMedia(job.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [job, invoice_number, getJobMedia, getBillMedia]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Space wrap>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (job) {
|
||||||
|
if (invoice_number) {
|
||||||
|
getBillMedia({ jobid: job.id, invoice_number });
|
||||||
|
} else {
|
||||||
|
getJobMedia(job.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SyncOutlined />
|
||||||
|
</Button>
|
||||||
|
<a href={CreateExplorerLinkForJob({ jobid: job.id })}>
|
||||||
|
<Button>{t("documents.labels.openinexplorer")}</Button>
|
||||||
|
</a>
|
||||||
|
<JobsDocumentsLocalGalleryReassign jobid={job.id} />
|
||||||
|
</Space>
|
||||||
|
<Card>
|
||||||
|
<DocumentsLocalUploadComponent
|
||||||
|
job={job}
|
||||||
|
invoice_number={invoice_number}
|
||||||
|
vendorid={vendorid}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
<Card title={t("jobs.labels.documents-images")}>
|
||||||
|
<Gallery
|
||||||
|
images={(allMedia && allMedia[job.id]) || []}
|
||||||
|
backdropClosesModal={true}
|
||||||
|
onSelectImage={(index, image) => {
|
||||||
|
toggleMediaSelected({ jobid: job.id, filename: image.filename });
|
||||||
|
}}
|
||||||
|
onClickImage={(props) => {
|
||||||
|
window.open(
|
||||||
|
props.target.src,
|
||||||
|
"_blank",
|
||||||
|
"toolbar=0,location=0,menubar=0"
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import { Button, Form, Popover, Space } from "antd";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { getJobMedia } from "../../redux/media/media.actions";
|
||||||
|
import { selectAllMedia } from "../../redux/media/media.selectors";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import cleanAxios from "../../utils/CleanAxios";
|
||||||
|
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
allMedia: selectAllMedia,
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
getJobMedia: (id) => dispatch(getJobMedia(id)),
|
||||||
|
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(JobsDocumentsLocalGalleryReassign);
|
||||||
|
|
||||||
|
export function JobsDocumentsLocalGalleryReassign({
|
||||||
|
bodyshop,
|
||||||
|
jobid,
|
||||||
|
allMedia,
|
||||||
|
getJobMedia,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const handleFinish = async ({ jobid: newJobid }) => {
|
||||||
|
setLoading(true);
|
||||||
|
const selectedDocuments = allMedia[jobid].filter((m) => m.isSelected);
|
||||||
|
|
||||||
|
await cleanAxios.post(`${bodyshop.localmediaserverhttp}/jobs/move`, {
|
||||||
|
from_jobid: jobid,
|
||||||
|
jobid: newJobid,
|
||||||
|
files: selectedDocuments.map((f) => f.filename),
|
||||||
|
});
|
||||||
|
|
||||||
|
getJobMedia(jobid);
|
||||||
|
setVisible(false);
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const popContent = (
|
||||||
|
<div>
|
||||||
|
<Form onFinish={handleFinish} layout="vertical" form={form}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("documents.labels.newjobid")}
|
||||||
|
style={{ width: "20rem" }}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={"jobid"}
|
||||||
|
>
|
||||||
|
<JobSearchSelect />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
<Space>
|
||||||
|
<Button type="primary" onClick={() => form.submit()}>
|
||||||
|
{t("general.actions.submit")}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => setVisible(false)}>
|
||||||
|
{t("general.actions.cancel")}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover content={popContent} visible={visible}>
|
||||||
|
<Button
|
||||||
|
//disabled={selectedImages.length < 1}
|
||||||
|
onClick={() => setVisible(true)}
|
||||||
|
loading={loading}
|
||||||
|
>
|
||||||
|
{t("documents.actions.reassign")}
|
||||||
|
</Button>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ export function NotesPresetButton({ bodyshop, form }) {
|
|||||||
const menu = (
|
const menu = (
|
||||||
<Menu>
|
<Menu>
|
||||||
{bodyshop.md_notes_presets.map((i, idx) => (
|
{bodyshop.md_notes_presets.map((i, idx) => (
|
||||||
<Menu.Item onClick={() => handleSelect(i)} onItemHover key={idx}>
|
<Menu.Item onClick={() => handleSelect(i)} key={idx}>
|
||||||
{i.label}
|
{i.label}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ import { connect } from "react-redux";
|
|||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import ScoreboardAddButton from "../job-scoreboard-add-button/job-scoreboard-add-button.component";
|
import ScoreboardAddButton from "../job-scoreboard-add-button/job-scoreboard-add-button.component";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setPrintCenterContext: (context) =>
|
setPrintCenterContext: (context) =>
|
||||||
@@ -35,7 +36,11 @@ export default connect(
|
|||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(ProductionListDetail);
|
)(ProductionListDetail);
|
||||||
|
|
||||||
export function ProductionListDetail({ jobs, setPrintCenterContext }) {
|
export function ProductionListDetail({
|
||||||
|
bodyshop,
|
||||||
|
jobs,
|
||||||
|
setPrintCenterContext,
|
||||||
|
}) {
|
||||||
const search = queryString.parse(useLocation().search);
|
const search = queryString.parse(useLocation().search);
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { selected } = search;
|
const { selected } = search;
|
||||||
@@ -144,11 +149,12 @@ export function ProductionListDetail({ jobs, setPrintCenterContext }) {
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
data={data ? data.jobs_by_pk : null}
|
data={data ? data.jobs_by_pk : null}
|
||||||
/>
|
/>
|
||||||
|
{!bodyshop.uselocalmediaserver && (
|
||||||
<JobDetailCardsDocumentsComponent
|
<JobDetailCardsDocumentsComponent
|
||||||
loading={loading}
|
loading={loading}
|
||||||
data={data ? data.jobs_by_pk : null}
|
data={data ? data.jobs_by_pk : null}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|||||||
@@ -584,6 +584,25 @@ export default function ShopInfoGeneral({ form }) {
|
|||||||
>
|
>
|
||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["uselocalmediaserver"]}
|
||||||
|
label={t("bodyshop.fields.uselocalmediaserver")}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["localmediaserverhttp"]}
|
||||||
|
label={t("bodyshop.fields.localmediaserverhttp")}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["localmediaservernetwork"]}
|
||||||
|
label={t("bodyshop.fields.localmediaservernetwork")}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}>
|
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}>
|
||||||
<Form.List name={["md_messaging_presets"]}>
|
<Form.List name={["md_messaging_presets"]}>
|
||||||
|
|||||||
@@ -107,6 +107,9 @@ export const QUERY_BODYSHOP = gql`
|
|||||||
md_parts_order_comment
|
md_parts_order_comment
|
||||||
bill_allow_post_to_closed
|
bill_allow_post_to_closed
|
||||||
md_to_emails
|
md_to_emails
|
||||||
|
uselocalmediaserver
|
||||||
|
localmediaserverhttp
|
||||||
|
localmediaservernetwork
|
||||||
employees {
|
employees {
|
||||||
user_email
|
user_email
|
||||||
id
|
id
|
||||||
@@ -212,6 +215,9 @@ export const UPDATE_SHOP = gql`
|
|||||||
md_parts_order_comment
|
md_parts_order_comment
|
||||||
bill_allow_post_to_closed
|
bill_allow_post_to_closed
|
||||||
md_to_emails
|
md_to_emails
|
||||||
|
uselocalmediaserver
|
||||||
|
localmediaserverhttp
|
||||||
|
localmediaservernetwork
|
||||||
employees {
|
employees {
|
||||||
id
|
id
|
||||||
first_name
|
first_name
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component";
|
import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component";
|
||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
|
import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -62,6 +63,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
dispatch(insertAuditTrail({ jobid, operation })),
|
||||||
});
|
});
|
||||||
export function JobsDetailPage({
|
export function JobsDetailPage({
|
||||||
|
bodyshop,
|
||||||
setPrintCenterContext,
|
setPrintCenterContext,
|
||||||
jobRO,
|
jobRO,
|
||||||
job,
|
job,
|
||||||
@@ -344,7 +346,11 @@ export function JobsDetailPage({
|
|||||||
}
|
}
|
||||||
key="documents"
|
key="documents"
|
||||||
>
|
>
|
||||||
<JobsDocumentsGalleryContainer jobId={job.id} />
|
{bodyshop.uselocalmediaserver ? (
|
||||||
|
<JobsDocumentsLocalGallery job={job} />
|
||||||
|
) : (
|
||||||
|
<JobsDocumentsGalleryContainer jobId={job.id} />
|
||||||
|
)}
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
<Tabs.TabPane
|
<Tabs.TabPane
|
||||||
tab={
|
tab={
|
||||||
|
|||||||
@@ -5,15 +5,34 @@ import JobsDocumentsComponent from "../../components/jobs-documents-gallery/jobs
|
|||||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||||
import { QUERY_TEMPORARY_DOCS } from "../../graphql/documents.queries";
|
import { QUERY_TEMPORARY_DOCS } from "../../graphql/documents.queries";
|
||||||
|
|
||||||
export default function TemporaryDocsComponent() {
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(TemporaryDocsComponent);
|
||||||
|
|
||||||
|
export function TemporaryDocsComponent({ bodyshop }) {
|
||||||
const { loading, error, data, refetch } = useQuery(QUERY_TEMPORARY_DOCS, {
|
const { loading, error, data, refetch } = useQuery(QUERY_TEMPORARY_DOCS, {
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
|
skip: bodyshop.uselocalmediaserver,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (loading) return <LoadingSpinner />;
|
if (loading) return <LoadingSpinner />;
|
||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
|
if (bodyshop.uselocalmediaserver) {
|
||||||
|
return <JobsDocumentsLocalGallery job={{ id: "temporary" }} />;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<JobsDocumentsComponent
|
<JobsDocumentsComponent
|
||||||
data={data ? data.documents : []}
|
data={data ? data.documents : []}
|
||||||
|
|||||||
34
client/src/redux/media/media.actions.js
Normal file
34
client/src/redux/media/media.actions.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import MediaActionTypes from "./media.types";
|
||||||
|
|
||||||
|
export const getJobMedia = (jobid) => ({
|
||||||
|
type: MediaActionTypes.GET_MEDIA_FOR_JOB,
|
||||||
|
payload: jobid,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getBillMedia = ({ jobid, invoice_number }) => {
|
||||||
|
console.log("in the action");
|
||||||
|
return {
|
||||||
|
type: MediaActionTypes.GET_MEDIA_FOR_BILL,
|
||||||
|
payload: { jobid, invoice_number },
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setJobMedia = ({ jobid, media }) => ({
|
||||||
|
type: MediaActionTypes.SET_MEDIA_FOR_JOB,
|
||||||
|
payload: { jobid, media },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const addMediaForJob = ({ jobid, media }) => ({
|
||||||
|
type: MediaActionTypes.ADD_MEDIA_FOR_JOB,
|
||||||
|
payload: { jobid, media },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getJobMediaError = ({ error, message }) => ({
|
||||||
|
type: MediaActionTypes.GET_MEDIA_FOR_JOB_ERROR,
|
||||||
|
payload: { error, message },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const toggleMediaSelected = ({ jobid, filename }) => ({
|
||||||
|
type: MediaActionTypes.TOGGLE_MEDIA_SELECTED,
|
||||||
|
payload: { jobid, filename },
|
||||||
|
});
|
||||||
34
client/src/redux/media/media.reducer.js
Normal file
34
client/src/redux/media/media.reducer.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import MediaActionTypes from "./media.types";
|
||||||
|
|
||||||
|
const INITIAL_STATE = { error: null };
|
||||||
|
|
||||||
|
const mediaReducer = (state = INITIAL_STATE, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case MediaActionTypes.SET_MEDIA_FOR_JOB:
|
||||||
|
return { ...state, [action.payload.jobid]: action.payload.media };
|
||||||
|
case MediaActionTypes.GET_MEDIA_FOR_JOB_ERROR:
|
||||||
|
return { ...state, error: action.payload };
|
||||||
|
case MediaActionTypes.ADD_MEDIA_FOR_JOB:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
[action.payload.jobid]: [
|
||||||
|
...(state[action.payload.jobid] ? state[action.payload.jobid] : []),
|
||||||
|
...(action.payload.media || []),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
case MediaActionTypes.TOGGLE_MEDIA_SELECTED:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
[action.payload.jobid]: state[action.payload.jobid].map((p) => {
|
||||||
|
if (p.filename === action.payload.filename) {
|
||||||
|
p.isSelected = !p.isSelected;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default mediaReducer;
|
||||||
108
client/src/redux/media/media.sagas.js
Normal file
108
client/src/redux/media/media.sagas.js
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import { all, call, takeLatest, put, select } from "redux-saga/effects";
|
||||||
|
import { getJobMediaError, setJobMedia } from "./media.actions";
|
||||||
|
import MediaActionTypes from "./media.types";
|
||||||
|
import cleanAxios from "../../utils/CleanAxios";
|
||||||
|
import normalizeUrl from "normalize-url";
|
||||||
|
|
||||||
|
export function* onSetJobMedia() {
|
||||||
|
yield takeLatest(MediaActionTypes.GET_MEDIA_FOR_JOB, getJobMedia);
|
||||||
|
}
|
||||||
|
export function* getJobMedia({ payload: jobid }) {
|
||||||
|
try {
|
||||||
|
const localmediaserverhttp = (yield select(
|
||||||
|
(state) => state.user.bodyshop.localmediaserverhttp
|
||||||
|
)).trim();
|
||||||
|
|
||||||
|
if (localmediaserverhttp && localmediaserverhttp !== "") {
|
||||||
|
const imagesFetch = yield cleanAxios.post(
|
||||||
|
`${localmediaserverhttp}/jobs/list`,
|
||||||
|
{
|
||||||
|
jobid,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const documentsFetch = yield cleanAxios.post(
|
||||||
|
`${localmediaserverhttp}/bills/list`,
|
||||||
|
{
|
||||||
|
jobid,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
yield put(
|
||||||
|
setJobMedia({
|
||||||
|
jobid,
|
||||||
|
media: [
|
||||||
|
...imagesFetch.data.map((d, idx) => {
|
||||||
|
return {
|
||||||
|
...d,
|
||||||
|
src: normalizeUrl(`${localmediaserverhttp}/${d.src}`),
|
||||||
|
thumbnail: normalizeUrl(
|
||||||
|
`${localmediaserverhttp}/${d.thumbnail}`
|
||||||
|
),
|
||||||
|
isSelected: false,
|
||||||
|
key: idx,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
...documentsFetch.data.map((d, idx) => {
|
||||||
|
return {
|
||||||
|
...d,
|
||||||
|
src: normalizeUrl(`${localmediaserverhttp}/${d.src}`),
|
||||||
|
thumbnail: normalizeUrl(
|
||||||
|
`${localmediaserverhttp}/${d.thumbnail}`
|
||||||
|
),
|
||||||
|
isSelected: false,
|
||||||
|
key: idx,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
yield put(getJobMediaError(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function* onSetBillMedia() {
|
||||||
|
yield takeLatest(MediaActionTypes.GET_MEDIA_FOR_BILL, getBillMedia);
|
||||||
|
}
|
||||||
|
export function* getBillMedia({ payload: { jobid, invoice_number } }) {
|
||||||
|
try {
|
||||||
|
const localmediaserverhttp = (yield select(
|
||||||
|
(state) => state.user.bodyshop.localmediaserverhttp
|
||||||
|
)).trim();
|
||||||
|
|
||||||
|
if (localmediaserverhttp && localmediaserverhttp !== "") {
|
||||||
|
const documentsFetch = yield cleanAxios.post(
|
||||||
|
`${localmediaserverhttp}/bills/list`,
|
||||||
|
{
|
||||||
|
jobid,
|
||||||
|
invoice_number,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
yield put(
|
||||||
|
setJobMedia({
|
||||||
|
jobid,
|
||||||
|
media: [
|
||||||
|
...documentsFetch.data.map((d, idx) => {
|
||||||
|
return {
|
||||||
|
...d,
|
||||||
|
src: normalizeUrl(`${localmediaserverhttp}/${d.src}`),
|
||||||
|
thumbnail: normalizeUrl(
|
||||||
|
`${localmediaserverhttp}/${d.thumbnail}`
|
||||||
|
),
|
||||||
|
isSelected: false,
|
||||||
|
key: idx,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
yield put(getJobMediaError(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* mediaSagas() {
|
||||||
|
yield all([call(onSetJobMedia), call(onSetBillMedia)]);
|
||||||
|
}
|
||||||
5
client/src/redux/media/media.selectors.js
Normal file
5
client/src/redux/media/media.selectors.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { createSelector } from "reselect";
|
||||||
|
|
||||||
|
const selectMedia = (state) => state.media;
|
||||||
|
|
||||||
|
export const selectAllMedia = createSelector([selectMedia], (media) => media);
|
||||||
12
client/src/redux/media/media.types.js
Normal file
12
client/src/redux/media/media.types.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const MediaActionTypes = {
|
||||||
|
SET_MEDIA_FOR_JOB: "SET_MEDIA_FOR_JOB",
|
||||||
|
GET_MEDIA_FOR_JOB: "GET_MEDIA_FOR_JOB",
|
||||||
|
GET_MEDIA_FOR_JOB_ERROR: "GET_MEDIA_FOR_JOB_ERROR",
|
||||||
|
ADD_MEDIA_FOR_JOB: "ADD_MEDIA_FOR_JOB",
|
||||||
|
TOGGLE_MEDIA_SELECTED: "TOGGLE_MEDIA_SELECTED",
|
||||||
|
POST_MEDIA_FOR_JOB: "POST_MEDIA_FOR_JOB",
|
||||||
|
POST_MEDIA_FOR_JOB_SUCCESS: "POST_MEDIA_FOR_JOB_SUCCESS",
|
||||||
|
POST_MEDIA_FOR_JOB_ERROR: "POST_MEDIA_FOR_JOB_ERROR",
|
||||||
|
GET_MEDIA_FOR_BILL: "GET_MEDIA_FOR_BILL",
|
||||||
|
};
|
||||||
|
export default MediaActionTypes;
|
||||||
@@ -4,6 +4,7 @@ import storage from "redux-persist/lib/storage";
|
|||||||
import { withReduxStateSync } from "redux-state-sync";
|
import { withReduxStateSync } from "redux-state-sync";
|
||||||
import applicationReducer from "./application/application.reducer";
|
import applicationReducer from "./application/application.reducer";
|
||||||
import emailReducer from "./email/email.reducer";
|
import emailReducer from "./email/email.reducer";
|
||||||
|
import mediaReducer from "./media/media.reducer";
|
||||||
import messagingReducer from "./messaging/messaging.reducer";
|
import messagingReducer from "./messaging/messaging.reducer";
|
||||||
import modalsReducer from "./modals/modals.reducer";
|
import modalsReducer from "./modals/modals.reducer";
|
||||||
import techReducer from "./tech/tech.reducer";
|
import techReducer from "./tech/tech.reducer";
|
||||||
@@ -29,6 +30,7 @@ const rootReducer = combineReducers({
|
|||||||
modals: modalsReducer,
|
modals: modalsReducer,
|
||||||
application: persistReducer(applicationPersistConfig, applicationReducer),
|
application: persistReducer(applicationPersistConfig, applicationReducer),
|
||||||
tech: techReducer,
|
tech: techReducer,
|
||||||
|
media: mediaReducer,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default withReduxStateSync(
|
export default withReduxStateSync(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { emailSagas } from "./email/email.sagas";
|
|||||||
import { modalsSagas } from "./modals/modals.sagas";
|
import { modalsSagas } from "./modals/modals.sagas";
|
||||||
import { applicationSagas } from "./application/application.sagas";
|
import { applicationSagas } from "./application/application.sagas";
|
||||||
import { techSagas } from "./tech/tech.sagas";
|
import { techSagas } from "./tech/tech.sagas";
|
||||||
|
import { mediaSagas } from "./media/media.sagas";
|
||||||
|
|
||||||
export default function* rootSaga() {
|
export default function* rootSaga() {
|
||||||
yield all([
|
yield all([
|
||||||
@@ -15,5 +16,6 @@ export default function* rootSaga() {
|
|||||||
call(modalsSagas),
|
call(modalsSagas),
|
||||||
call(applicationSagas),
|
call(applicationSagas),
|
||||||
call(techSagas),
|
call(techSagas),
|
||||||
|
call(mediaSagas),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,10 @@ const userReducer = (state = INITIAL_STATE, action) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
case UserActionTypes.SET_SHOP_DETAILS:
|
case UserActionTypes.SET_SHOP_DETAILS:
|
||||||
return { ...state, bodyshop: action.payload };
|
return {
|
||||||
|
...state,
|
||||||
|
bodyshop: action.payload,
|
||||||
|
};
|
||||||
case UserActionTypes.SIGN_IN_FAILURE:
|
case UserActionTypes.SIGN_IN_FAILURE:
|
||||||
case UserActionTypes.SIGN_OUT_FAILURE:
|
case UserActionTypes.SIGN_OUT_FAILURE:
|
||||||
case UserActionTypes.EMAIL_SIGN_UP_FAILURE:
|
case UserActionTypes.EMAIL_SIGN_UP_FAILURE:
|
||||||
|
|||||||
@@ -282,6 +282,8 @@
|
|||||||
},
|
},
|
||||||
"last_name_first": "Display Owner Info as <Last>, <First>",
|
"last_name_first": "Display Owner Info as <Last>, <First>",
|
||||||
"lastnumberworkingdays": "Scoreboard - Last Number of Working Days",
|
"lastnumberworkingdays": "Scoreboard - Last Number of Working Days",
|
||||||
|
"localmediaserverhttp": "Local Media Server - HTTP Path",
|
||||||
|
"localmediaservernetwork": "Local Media Server - Network Path",
|
||||||
"logo_img_footer_margin": "Footer Margin (px)",
|
"logo_img_footer_margin": "Footer Margin (px)",
|
||||||
"logo_img_header_margin": "Header Margin (px)",
|
"logo_img_header_margin": "Header Margin (px)",
|
||||||
"logo_img_path": "Shop Logo",
|
"logo_img_path": "Shop Logo",
|
||||||
@@ -507,6 +509,7 @@
|
|||||||
"timezone": "Timezone",
|
"timezone": "Timezone",
|
||||||
"tt_allow_post_to_invoiced": "Allow Time Tickets to be posted to Invoiced & Exported Jobs",
|
"tt_allow_post_to_invoiced": "Allow Time Tickets to be posted to Invoiced & Exported Jobs",
|
||||||
"use_fippa": "Use FIPPA for Names on Generated Documents?",
|
"use_fippa": "Use FIPPA for Names on Generated Documents?",
|
||||||
|
"uselocalmediaserver": "Use Local Media Server?",
|
||||||
"website": "Website",
|
"website": "Website",
|
||||||
"zip_post": "Zip/Postal Code"
|
"zip_post": "Zip/Postal Code"
|
||||||
},
|
},
|
||||||
@@ -824,6 +827,7 @@
|
|||||||
"confirmdelete": "Are you sure you want to delete these documents. This CANNOT be undone.",
|
"confirmdelete": "Are you sure you want to delete these documents. This CANNOT be undone.",
|
||||||
"doctype": "Document Type",
|
"doctype": "Document Type",
|
||||||
"newjobid": "Assign to Job",
|
"newjobid": "Assign to Job",
|
||||||
|
"openinexplorer": "Open in Explorer",
|
||||||
"reassign_limitexceeded": "Reassigning all selected documents will exceed the job storage limit for your shop. ",
|
"reassign_limitexceeded": "Reassigning all selected documents will exceed the job storage limit for your shop. ",
|
||||||
"reassign_limitexceeded_title": "Unable to reassign document(s)",
|
"reassign_limitexceeded_title": "Unable to reassign document(s)",
|
||||||
"storageexceeded": "You've exceeded your storage limit for this job. Please remove documents, or increase your storage plan.",
|
"storageexceeded": "You've exceeded your storage limit for this job. Please remove documents, or increase your storage plan.",
|
||||||
|
|||||||
@@ -282,6 +282,8 @@
|
|||||||
},
|
},
|
||||||
"last_name_first": "",
|
"last_name_first": "",
|
||||||
"lastnumberworkingdays": "",
|
"lastnumberworkingdays": "",
|
||||||
|
"localmediaserverhttp": "",
|
||||||
|
"localmediaservernetwork": "",
|
||||||
"logo_img_footer_margin": "",
|
"logo_img_footer_margin": "",
|
||||||
"logo_img_header_margin": "",
|
"logo_img_header_margin": "",
|
||||||
"logo_img_path": "",
|
"logo_img_path": "",
|
||||||
@@ -507,6 +509,7 @@
|
|||||||
"timezone": "",
|
"timezone": "",
|
||||||
"tt_allow_post_to_invoiced": "",
|
"tt_allow_post_to_invoiced": "",
|
||||||
"use_fippa": "",
|
"use_fippa": "",
|
||||||
|
"uselocalmediaserver": "",
|
||||||
"website": "",
|
"website": "",
|
||||||
"zip_post": ""
|
"zip_post": ""
|
||||||
},
|
},
|
||||||
@@ -824,6 +827,7 @@
|
|||||||
"confirmdelete": "",
|
"confirmdelete": "",
|
||||||
"doctype": "",
|
"doctype": "",
|
||||||
"newjobid": "",
|
"newjobid": "",
|
||||||
|
"openinexplorer": "",
|
||||||
"reassign_limitexceeded": "",
|
"reassign_limitexceeded": "",
|
||||||
"reassign_limitexceeded_title": "",
|
"reassign_limitexceeded_title": "",
|
||||||
"storageexceeded": "",
|
"storageexceeded": "",
|
||||||
|
|||||||
@@ -282,6 +282,8 @@
|
|||||||
},
|
},
|
||||||
"last_name_first": "",
|
"last_name_first": "",
|
||||||
"lastnumberworkingdays": "",
|
"lastnumberworkingdays": "",
|
||||||
|
"localmediaserverhttp": "",
|
||||||
|
"localmediaservernetwork": "",
|
||||||
"logo_img_footer_margin": "",
|
"logo_img_footer_margin": "",
|
||||||
"logo_img_header_margin": "",
|
"logo_img_header_margin": "",
|
||||||
"logo_img_path": "",
|
"logo_img_path": "",
|
||||||
@@ -507,6 +509,7 @@
|
|||||||
"timezone": "",
|
"timezone": "",
|
||||||
"tt_allow_post_to_invoiced": "",
|
"tt_allow_post_to_invoiced": "",
|
||||||
"use_fippa": "",
|
"use_fippa": "",
|
||||||
|
"uselocalmediaserver": "",
|
||||||
"website": "",
|
"website": "",
|
||||||
"zip_post": ""
|
"zip_post": ""
|
||||||
},
|
},
|
||||||
@@ -824,6 +827,7 @@
|
|||||||
"confirmdelete": "",
|
"confirmdelete": "",
|
||||||
"doctype": "",
|
"doctype": "",
|
||||||
"newjobid": "",
|
"newjobid": "",
|
||||||
|
"openinexplorer": "",
|
||||||
"reassign_limitexceeded": "",
|
"reassign_limitexceeded": "",
|
||||||
"reassign_limitexceeded_title": "",
|
"reassign_limitexceeded_title": "",
|
||||||
"storageexceeded": "",
|
"storageexceeded": "",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default async function RenderTemplate(
|
|||||||
let { contextData, useShopSpecificTemplate } = await fetchContextData(
|
let { contextData, useShopSpecificTemplate } = await fetchContextData(
|
||||||
templateObject
|
templateObject
|
||||||
);
|
);
|
||||||
console.log(templateObject.name);
|
|
||||||
const { ignoreCustomMargins } = Templates[templateObject.name];
|
const { ignoreCustomMargins } = Templates[templateObject.name];
|
||||||
|
|
||||||
let reportRequest = {
|
let reportRequest = {
|
||||||
|
|||||||
6
client/src/utils/localmedia.js
Normal file
6
client/src/utils/localmedia.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { store } from "../redux/store";
|
||||||
|
|
||||||
|
export function CreateExplorerLinkForJob({ jobid }) {
|
||||||
|
const bodyshop = store.getState().user.bodyshop;
|
||||||
|
return `imexmedia://${bodyshop.localmediaservernetwork}/Jobs/${jobid}`;
|
||||||
|
}
|
||||||
@@ -9702,6 +9702,11 @@ normalize-url@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
|
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
|
||||||
integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
|
integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
|
||||||
|
|
||||||
|
normalize-url@^7.0.3:
|
||||||
|
version "7.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-7.0.3.tgz#12e56889f7a54b2d5b09616f36c442a9063f61af"
|
||||||
|
integrity sha512-RiCOdwdPnzvwcBFJE4iI1ss3dMVRIrEzFpn8ftje6iBfzBInqlnRrNhxcLwBEKjPPXQKzm1Ptlxtaiv9wdcj5w==
|
||||||
|
|
||||||
npm-run-path@^2.0.0:
|
npm-run-path@^2.0.0:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
|
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
|
||||||
|
|||||||
@@ -835,6 +835,8 @@
|
|||||||
- jc_hourly_rates
|
- jc_hourly_rates
|
||||||
- jobsizelimit
|
- jobsizelimit
|
||||||
- last_name_first
|
- last_name_first
|
||||||
|
- localmediaserverhttp
|
||||||
|
- localmediaservernetwork
|
||||||
- logo_img_path
|
- logo_img_path
|
||||||
- md_categories
|
- md_categories
|
||||||
- md_ccc_rates
|
- md_ccc_rates
|
||||||
@@ -885,6 +887,7 @@
|
|||||||
- tt_allow_post_to_invoiced
|
- tt_allow_post_to_invoiced
|
||||||
- updated_at
|
- updated_at
|
||||||
- use_fippa
|
- use_fippa
|
||||||
|
- uselocalmediaserver
|
||||||
- website
|
- website
|
||||||
- workingdays
|
- workingdays
|
||||||
- zip_post
|
- zip_post
|
||||||
@@ -922,6 +925,8 @@
|
|||||||
- intakechecklist
|
- intakechecklist
|
||||||
- jc_hourly_rates
|
- jc_hourly_rates
|
||||||
- last_name_first
|
- last_name_first
|
||||||
|
- localmediaserverhttp
|
||||||
|
- localmediaservernetwork
|
||||||
- logo_img_path
|
- logo_img_path
|
||||||
- md_categories
|
- md_categories
|
||||||
- md_ccc_rates
|
- md_ccc_rates
|
||||||
@@ -965,6 +970,7 @@
|
|||||||
- tt_allow_post_to_invoiced
|
- tt_allow_post_to_invoiced
|
||||||
- updated_at
|
- updated_at
|
||||||
- use_fippa
|
- use_fippa
|
||||||
|
- uselocalmediaserver
|
||||||
- website
|
- website
|
||||||
- workingdays
|
- workingdays
|
||||||
- zip_post
|
- zip_post
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."bodyshops" add column "uselocalmediaserver" boolean
|
||||||
|
-- not null default 'false';
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."bodyshops" add column "uselocalmediaserver" boolean
|
||||||
|
not null default 'false';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."bodyshops" add column "localmediaserverhttp" text
|
||||||
|
-- null;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."bodyshops" add column "localmediaserverhttp" text
|
||||||
|
null;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."bodyshops" add column "localmediaservernetwork" text
|
||||||
|
-- null;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."bodyshops" add column "localmediaservernetwork" text
|
||||||
|
null;
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "12.22.6",
|
"node": "16.15.0",
|
||||||
"npm": "7.17.0"
|
"npm": "7.17.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Reference in New Issue
Block a user