From 79dce5d0698244a0872f1540ae8fe35879cb346e Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Thu, 28 Dec 2023 17:33:21 -0500 Subject: [PATCH] Allow for Component Token Overrides. Signed-off-by: Dave Richer --- .../bill-line-search-select.component.jsx | 2 +- .../breadcrumbs/breadcrumbs.component.jsx | 87 +- .../chat-presets/chat-presets.component.jsx | 16 +- .../chat-tag-ro/chat-tag-ro.component.jsx | 2 +- ...ontracts-rates-change-button.component.jsx | 16 +- .../courtesy-cars-list.component.jsx | 41 +- .../dashboard-grid.component.jsx | 22 +- .../dms-log-events.component.jsx | 18 +- .../dms-post-form/dms-post-form.component.jsx | 865 +++++++++--------- .../email-overlay/email-overlay.component.jsx | 500 +++++----- .../job-at-change/job-at-change.component.jsx | 21 +- .../schedule-event.color.component.jsx | 25 +- .../schedule-event.component.jsx | 100 +- .../job-detail-cards.dates.component.jsx | 297 +++--- .../job-lines-expander.component.jsx | 175 ++-- .../job-detail-lines/job-lines.component.jsx | 21 +- .../job-line-location-popup.component.jsx | 2 +- .../job-line-status-popup.component.jsx | 2 +- .../job-lines-preset-button.component.jsx | 34 +- .../jobs-admin-change.status.component.jsx | 12 +- .../jobs-change-status.component.jsx | 31 +- .../jobs-close-auto-allocate.component.jsx | 17 +- ...jobs-detail-change-estimator.component.jsx | 19 +- ...bs-detail-change-filehandler.component.jsx | 16 +- .../jobs-detail-header-actions.component.jsx | 2 +- ...s-detail-rates-change-button.component.jsx | 19 +- .../notes-preset-button.component.jsx | 20 +- ...rts-order-modal-price-change.component.jsx | 2 +- .../parts-order-modal.component.jsx | 19 +- .../production-list-columns.add.component.jsx | 17 +- ...roduction-list-columns.alert.component.jsx | 20 +- ...on-list-columns.bodypriority.component.jsx | 40 +- ...production-list-columns.date.component.jsx | 2 +- ...-list-columns.detailpriority.component.jsx | 40 +- ...n-list-columns.lastcontacted.component.jsx | 1 + ...n-list-columns.paintpriority.component.jsx | 40 +- ...roduction-list-columns.status.category.jsx | 12 +- ...oduction-list-columns.status.component.jsx | 12 +- .../production-list-print.component.jsx | 130 +-- ...ction-list-table-view-select.component.jsx | 2 +- .../production-list-table.component.jsx | 16 +- .../schedule-block-day.component.jsx | 14 +- ...e-existing-appointments-list.component.jsx | 77 +- .../scoreboard-entry-edit.component.jsx | 2 + .../shop-info/shop-info.component.jsx | 110 ++- .../shop-template-add.component.jsx | 28 +- .../shop-templates-list.container.jsx | 6 +- .../tech-lookup-jobs-drawer.component.jsx | 227 ++--- .../tech-sider/tech-sider.component.jsx | 98 +- .../vendor-search-select.component.jsx | 2 +- client/src/landing/Nav0.jsx | 5 +- .../contract-detail.page.component.jsx | 79 +- .../jobs-detail.page.component.jsx | 220 ++--- .../phonebook/phonebook.page.container.jsx | 26 +- .../scoreboard/scoreboard.page.container.jsx | 76 +- .../shop-vendor.page.component.jsx | 7 +- client/src/pages/shop/shop.page.component.jsx | 39 +- 57 files changed, 1890 insertions(+), 1861 deletions(-) diff --git a/client/src/components/bill-line-search-select/bill-line-search-select.component.jsx b/client/src/components/bill-line-search-select/bill-line-search-select.component.jsx index 552c2ad47..d985301b6 100644 --- a/client/src/components/bill-line-search-select/bill-line-search-select.component.jsx +++ b/client/src/components/bill-line-search-select/bill-line-search-select.component.jsx @@ -15,7 +15,7 @@ const BillLineSearchSelect = ( disabled={disabled} ref={ref} showSearch - dropdownMatchSelectWidth={false} + popupMatchSelectWidth={false} // optionFilterProp="line_desc" filterOption={(inputValue, option) => { return ( diff --git a/client/src/components/breadcrumbs/breadcrumbs.component.jsx b/client/src/components/breadcrumbs/breadcrumbs.component.jsx index d75ca44e2..360eb480d 100644 --- a/client/src/components/breadcrumbs/breadcrumbs.component.jsx +++ b/client/src/components/breadcrumbs/breadcrumbs.component.jsx @@ -1,55 +1,64 @@ -import { HomeFilled } from "@ant-design/icons"; -import { Breadcrumb, Row, Col } from "antd"; +import {HomeFilled} from "@ant-design/icons"; +import {Breadcrumb, Col, Row} from "antd"; import React from "react"; -import { connect } from "react-redux"; -import { Link } from "react-router-dom"; -import { createStructuredSelector } from "reselect"; -import { selectBreadcrumbs } from "../../redux/application/application.selectors"; -import { selectBodyshop } from "../../redux/user/user.selectors"; +import {connect} from "react-redux"; +import {Link} from "react-router-dom"; +import {createStructuredSelector} from "reselect"; +import {selectBreadcrumbs} from "../../redux/application/application.selectors"; +import {selectBodyshop} from "../../redux/user/user.selectors"; import GlobalSearch from "../global-search/global-search.component"; import GlobalSearchOs from "../global-search/global-search-os.component"; import "./breadcrumbs.styles.scss"; -import { useSplitTreatments } from "@splitsoftware/splitio-react"; +import {useSplitTreatments} from "@splitsoftware/splitio-react"; const mapStateToProps = createStructuredSelector({ - breadcrumbs: selectBreadcrumbs, - bodyshop: selectBodyshop, + breadcrumbs: selectBreadcrumbs, + bodyshop: selectBodyshop, }); -export function BreadCrumbs({ breadcrumbs, bodyshop }) { +export function BreadCrumbs({breadcrumbs, bodyshop}) { - const { treatments: {OpenSearch} } = useSplitTreatments({ + const {treatments: {OpenSearch}} = useSplitTreatments({ attributes: {}, names: ["OpenSearch"], splitKey: bodyshop && bodyshop.imexshopid, }); - + // TODO - Client Update - Technically key is not doing anything here return ( - - - - - - {" "} - {(bodyshop && bodyshop.shopname && `(${bodyshop.shopname})`) || - ""} - - - {breadcrumbs.map((item) => - item.link ? ( - - {item.label} - - ) : ( - {item.label} - ) - )} - - - - {OpenSearch.treatment === "on" ? : } - - - ); + + + + {" "} + {(bodyshop && bodyshop.shopname && `(${bodyshop.shopname})`) || + ""} + + ), + }, + ...breadcrumbs.map((item) => + item.link + ? { + key: item.label, + title: {item.label}, + } + : { + key: item.label, + title: item.label, + } + ), + ]} + /> + + + {OpenSearch.treatment === "on" ? : } + + + ); } + export default connect(mapStateToProps, null)(BreadCrumbs); diff --git a/client/src/components/chat-presets/chat-presets.component.jsx b/client/src/components/chat-presets/chat-presets.component.jsx index 74e1a0dd6..3b3bcf400 100644 --- a/client/src/components/chat-presets/chat-presets.component.jsx +++ b/client/src/components/chat-presets/chat-presets.component.jsx @@ -16,19 +16,19 @@ const mapDispatchToProps = (dispatch) => ({ }); export function ChatPresetsComponent({ bodyshop, setMessage, className }) { + const menuItems = bodyshop.md_messaging_presets.map((i, idx) => ({ + key: idx, + label: i.label, + onClick: () => setMessage(i.text), + })); + const menu = ( - - {bodyshop.md_messaging_presets.map((i, idx) => ( - setMessage(i.text)} key={idx}> - {i.label} - - ))} - + ); return (
- +
diff --git a/client/src/components/chat-tag-ro/chat-tag-ro.component.jsx b/client/src/components/chat-tag-ro/chat-tag-ro.component.jsx index ad9f64e02..b6d33962f 100644 --- a/client/src/components/chat-tag-ro/chat-tag-ro.component.jsx +++ b/client/src/components/chat-tag-ro/chat-tag-ro.component.jsx @@ -19,7 +19,7 @@ export default function ChatTagRoComponent({ - - - - - - - - + const handleFinish = (values) => { + socket.emit(`${determineDmsType(bodyshop)}-export-job`, { + jobid: job.id, + txEnvelope: values, + }); + console.log(logsRef); + if (logsRef) { + console.log("executing", logsRef); + logsRef.curent && + logsRef.current.scrollIntoView({ + behavior: "smooth", + }); + } + }; - {bodyshop.cdk_dealerid && ( -
- - - - - - - - - - - - - - - - - - - - - -
- )} - - - - - - - - - - - - {(fields, { add, remove }) => { - return ( -
- {fields.map((field, index) => ( - - - - - - - - - - - - - - - - {t("jobs.fields.dms.payer.controlnumber")}{" "} - - {bodyshop.cdk_configuration.controllist && - bodyshop.cdk_configuration.controllist.map( - (key, idx) => ( - { - form.setFieldsValue({ - payers: form - .getFieldValue("payers") - .map((row, mapIndex) => { - if (index !== mapIndex) - return row; - - return { - ...row, - controlnumber: - key.controlnumber, - }; - }), - }); - }} - > - {key.name} - - ) - )} -
- } - > - e.preventDefault()}> - - - - + return ( + +
+ + - - + > + + + + + + + + + - - {() => { - const payers = form.getFieldValue("payers"); - - const row = payers && payers[index]; - - const cdkPayer = - bodyshop.cdk_configuration.payers && - bodyshop.cdk_configuration.payers.find( - (i) => i && row && i.name === row.name - ); - if ( - i18n.exists(`jobs.fields.${cdkPayer?.control_type}`) - ) - return ( -
- {cdkPayer && - t(`jobs.fields.${cdkPayer?.control_type}`)} -
- ); - else if ( - i18n.exists( - `jobs.fields.dms.control_type.${cdkPayer?.control_type}` - ) - ) { - return ( -
- {cdkPayer && - t( - `jobs.fields.dms.control_type.${cdkPayer?.control_type}` - )} -
- ); - } else { - return null; - } - }} -
- - { - remove(field.name); - }} - /> - - - ))} - - - - - ); - }} - - - {() => { - //Perform Calculation to determine discrepancy. - let totalAllocated = Dinero(); - - const payers = form.getFieldValue("payers"); - payers && - payers.forEach((payer) => { - totalAllocated = totalAllocated.add( - Dinero({ amount: Math.round((payer?.amount || 0) * 100) }) - ); - }); - - const totals = - socket.allocationsSummary && - socket.allocationsSummary.reduce( - (acc, val) => { - return { - totalSale: acc.totalSale.add(Dinero(val.sale)), - totalCost: acc.totalCost.add(Dinero(val.cost)), - }; - }, - { - totalSale: Dinero(), - totalCost: Dinero(), - } - ); - const discrep = totals - ? totals.totalSale.subtract(totalAllocated) - : Dinero(); - return ( - - - - - - = - - - - ); - }} - - -
- ); + + + + + + + + + + + {(fields, {add, remove}) => { + return ( +
+ {fields.map((field, index) => ( + + + + + + + + + + + + + + + + {t("jobs.fields.dms.payer.controlnumber")}{" "} + ({ + key: idx, + label: key.name, + onClick: () => { + form.setFieldsValue({ + payers: form.getFieldValue("payers").map((row, mapIndex) => { + if (index !== mapIndex) return row; + return { + ...row, + controlnumber: key.controlnumber, + }; + }), + }); + }, + }))} + />} + > + e.preventDefault()}> + + + +
+ } + key={`${index}controlnumber`} + name={[field.name, "controlnumber"]} + rules={[ + { + required: true, + }, + ]} + > + + + + + {() => { + const payers = form.getFieldValue("payers"); + + const row = payers && payers[index]; + + const cdkPayer = + bodyshop.cdk_configuration.payers && + bodyshop.cdk_configuration.payers.find( + (i) => i && row && i.name === row.name + ); + if ( + i18n.exists(`jobs.fields.${cdkPayer?.control_type}`) + ) + return ( +
+ {cdkPayer && + t(`jobs.fields.${cdkPayer?.control_type}`)} +
+ ); + else if ( + i18n.exists( + `jobs.fields.dms.control_type.${cdkPayer?.control_type}` + ) + ) { + return ( +
+ {cdkPayer && + t( + `jobs.fields.dms.control_type.${cdkPayer?.control_type}` + )} +
+ ); + } else { + return null; + } + }} +
+ + { + remove(field.name); + }} + /> + + + ))} + + + + + ); + }} +
+ + {() => { + //Perform Calculation to determine discrepancy. + let totalAllocated = Dinero(); + + const payers = form.getFieldValue("payers"); + payers && + payers.forEach((payer) => { + totalAllocated = totalAllocated.add( + Dinero({amount: Math.round((payer?.amount || 0) * 100)}) + ); + }); + + const totals = + socket.allocationsSummary && + socket.allocationsSummary.reduce( + (acc, val) => { + return { + totalSale: acc.totalSale.add(Dinero(val.sale)), + totalCost: acc.totalCost.add(Dinero(val.cost)), + }; + }, + { + totalSale: Dinero(), + totalCost: Dinero(), + } + ); + const discrep = totals + ? totals.totalSale.subtract(totalAllocated) + : Dinero(); + return ( + + + - + + = + + + + ); + }} + + + + ); } diff --git a/client/src/components/email-overlay/email-overlay.component.jsx b/client/src/components/email-overlay/email-overlay.component.jsx index 362d973b4..38495df4f 100644 --- a/client/src/components/email-overlay/email-overlay.component.jsx +++ b/client/src/components/email-overlay/email-overlay.component.jsx @@ -1,267 +1,269 @@ -import { UploadOutlined, UserAddOutlined } from "@ant-design/icons"; -import { - Button, - Divider, - Dropdown, - Form, - Input, - Menu, - Select, - Space, - Tabs, - Upload, -} from "antd"; +import {UploadOutlined, UserAddOutlined} from "@ant-design/icons"; +import {Button, Divider, Dropdown, Form, Input, Menu, Select, Space, Tabs, Upload,} from "antd"; import _ from "lodash"; import React from "react"; -import { useTranslation } from "react-i18next"; -import { connect } from "react-redux"; -import { createStructuredSelector } from "reselect"; -import { selectEmailConfig } from "../../redux/email/email.selectors"; -import { - selectBodyshop, - selectCurrentUser, -} from "../../redux/user/user.selectors"; -import { CreateExplorerLinkForJob } from "../../utils/localmedia"; +import {useTranslation} from "react-i18next"; +import {connect} from "react-redux"; +import {createStructuredSelector} from "reselect"; +import {selectEmailConfig} from "../../redux/email/email.selectors"; +import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors"; +import {CreateExplorerLinkForJob} from "../../utils/localmedia"; import EmailDocumentsComponent from "../email-documents/email-documents.component"; const mapStateToProps = createStructuredSelector({ - bodyshop: selectBodyshop, - currentUser: selectCurrentUser, - emailConfig: selectEmailConfig, + bodyshop: selectBodyshop, + currentUser: selectCurrentUser, + emailConfig: selectEmailConfig, }); const mapDispatchToProps = (dispatch) => ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) + //setUserLanguage: language => dispatch(setUserLanguage(language)) }); export default connect( - mapStateToProps, - mapDispatchToProps + mapStateToProps, + mapDispatchToProps )(EmailOverlayComponent); export function EmailOverlayComponent({ - emailConfig, - form, - selectedMediaState, - bodyshop, - currentUser, -}) { - const { t } = useTranslation(); - const handleClick = ({ item, key, keyPath }) => { - const email = item.props.value; - form.setFieldsValue({ - to: _.uniq([ - ...form.getFieldValue("to"), - ...(typeof email === "string" ? [email] : email), - ]), - }); - }; - const handle_CC_Click = ({ item, key, keyPath }) => { - const email = item.props.value; - form.setFieldsValue({ - cc: _.uniq([ - ...(form.getFieldValue("cc") || ""), - ...(typeof email === "string" ? [email] : email), - ]), - }); - }; + emailConfig, + form, + selectedMediaState, + bodyshop, + currentUser, + }) { + const {t} = useTranslation(); + const handleClick = ({item, key, keyPath}) => { + const email = item.props.value; + form.setFieldsValue({ + to: _.uniq([ + ...form.getFieldValue("to"), + ...(typeof email === "string" ? [email] : email), + ]), + }); + }; + const handle_CC_Click = ({item, key, keyPath}) => { + const email = item.props.value; + form.setFieldsValue({ + cc: _.uniq([ + ...(form.getFieldValue("cc") || ""), + ...(typeof email === "string" ? [email] : email), + ]), + }); + }; - const menu = ( -
- - {bodyshop.employees - .filter((e) => e.user_email) - .map((e, idx) => ( - - {`${e.first_name} ${e.last_name}`} - - ))} - {bodyshop.md_to_emails.map((e, idx) => ( - - {e.label} - - ))} - -
- ); - - const menuCC = ( -
- - {bodyshop.employees - .filter((e) => e.user_email) - .map((e, idx) => ( - - {`${e.first_name} ${e.last_name}`} - - ))} - {bodyshop.md_to_emails.map((e, idx) => ( - - {e.label} - - ))} - -
- ); - - return ( -
- - - - - {t("emails.fields.to")} - - e.preventDefault()} - > - - - - - } - name="to" - rules={[ - { - required: true, - //message: t("general.validation.required"), - }, - ]} - > - - - - - - - {t("emails.labels.preview")} - {bodyshop.attach_pdf_to_email && ( - {t("emails.labels.pdfcopywillbeattached")} - )} - - - {() => { - return ( -
e.user_email) + .map((e, idx) => ({ + key: idx, + label: `${e.first_name} ${e.last_name}`, + value: e.user_email, + })), + ...bodyshop.md_to_emails.map((e, idx) => ({ + key: idx + "group", + label: e.label, + value: e.emails, + })), + ]} /> - ); - }} - + ); - - - - + const menuCC = ( +
+ e.user_email) + .map((e, idx) => ({ + key: idx, + label: `${e.first_name} ${e.last_name}`, + value: e.user_email, + })), + ...bodyshop.md_to_emails.map((e, idx) => ({ + key: idx + "group", + label: e.label, + value: e.emails, + })), + ]} + /> +
+ ); - - {bodyshop.uselocalmediaserver && emailConfig.jobid && ( - - - - )} - { - if (Array.isArray(e)) { - return e; - } - return e && e.fileList; - }} - rules={[ - ({ getFieldValue }) => ({ - validator(rule, value) { - const totalSize = value.reduce( - (acc, val) => (acc = acc + val.size), - 0 - ); - - const limit = - 10485760 - new Blob([form.getFieldValue("html")]).size; - - if (totalSize > limit) { - return Promise.reject(t("general.errors.sizelimit")); - } - return Promise.resolve(); - }, - }), - ]} - > - + - <> -

- -

-

- Click or drag files to this area to upload. -

- -
-
-
-
-
- ); + +
+ + {t("emails.fields.to")} + + e.preventDefault()} + > + + + + + } + name="to" + rules={[ + { + required: true, + //message: t("general.validation.required"), + }, + ]} + > + + + + + + + {t("emails.labels.preview")} + {bodyshop.attach_pdf_to_email && ( + {t("emails.labels.pdfcopywillbeattached")} + )} + + + {() => { + return ( +
+ ); + }} + + + + ), + }, + { + key: "attachments", + tab: t("emails.labels.attachments"), + children: ( + <> + {bodyshop.uselocalmediaserver && emailConfig.jobid && ( + + + + )} + { + if (Array.isArray(e)) { + return e; + } + return e && e.fileList; + }} + rules={[ + ({getFieldValue}) => ({ + validator(rule, value) { + const totalSize = value.reduce( + (acc, val) => (acc = acc + val.size), + 0 + ); + + const limit = + 10485760 - new Blob([form.getFieldValue("html")]).size; + + if (totalSize > limit) { + return Promise.reject(t("general.errors.sizelimit")); + } + return Promise.resolve(); + }, + }), + ]} + > + + <> +

+ +

+

+ Click or drag files to this area to upload. +

+ +
+
+ + ), + }, + ]} + /> +
+ ); } diff --git a/client/src/components/job-at-change/job-at-change.component.jsx b/client/src/components/job-at-change/job-at-change.component.jsx index bd6c556ef..0b0075ac6 100644 --- a/client/src/components/job-at-change/job-at-change.component.jsx +++ b/client/src/components/job-at-change/job-at-change.component.jsx @@ -38,17 +38,20 @@ export function JobAltTransportChange({ bodyshop, job }) { } }; const menu = ( - - {bodyshop.appt_alt_transport && - bodyshop.appt_alt_transport.map((alt) => ( - {alt} - ))} - - {t("general.actions.clear")} - + ({ + key: alt, + label: alt, + })), + { key: "null", label: t("general.actions.clear") }, + ]} + /> ); return ( - + e.preventDefault()}> diff --git a/client/src/components/job-at-change/schedule-event.color.component.jsx b/client/src/components/job-at-change/schedule-event.color.component.jsx index 632bbdfcf..b11c615de 100644 --- a/client/src/components/job-at-change/schedule-event.color.component.jsx +++ b/client/src/components/job-at-change/schedule-event.color.component.jsx @@ -45,20 +45,23 @@ export function ScheduleEventColor({ bodyshop, event }) { ?.label; const menu = ( - - {bodyshop.appt_colors && - bodyshop.appt_colors.map((color) => ( - - {color.label} - - ))} - - {t("general.actions.clear")} - + ({ + key: color.color.hex, + label: color.label, + style: { color: color.color.hex }, + })), + { key: "divider", label:
, disabled: true }, + { key: "null", label: t("general.actions.clear") }, + ]} + /> ); return ( - + e.preventDefault()}> {selectedColor} diff --git a/client/src/components/job-at-change/schedule-event.component.jsx b/client/src/components/job-at-change/schedule-event.component.jsx index 2d3e56547..9cc3fceda 100644 --- a/client/src/components/job-at-change/schedule-event.component.jsx +++ b/client/src/components/job-at-change/schedule-event.component.jsx @@ -185,55 +185,57 @@ export function ScheduleEventComponent({ ) : null} {event.job ? ( - { - const Template = TemplateList("job").appointment_reminder; - GenerateDocument( - { - name: Template.key, - variables: { id: event.job.id }, - }, - { - to: event.job && event.job.ownr_ea, - subject: Template.subject, - }, - "e", - event.job && event.job.id - ); - }} - disabled={event.arrived} - > - {t("general.labels.email")} - - { - const p = parsePhoneNumber(event.job.ownr_ph1, "CA"); - if (p && p.isValid()) { - openChatByPhone({ - phone_num: p.formatInternational(), - jobid: event.job.id, - }); - setMessage( - t("appointments.labels.reminder", { - shopname: bodyshop.shopname, - date: dayjs(event.start).format("MM/DD/YYYY"), - time: dayjs(event.start).format("HH:mm a"), - }) - ); - setVisible(false); - } else { - notification["error"]({ - message: t("messaging.error.invalidphone"), - }); - } - }} - disabled={event.arrived || !bodyshop.messagingservicesid} - > - {t("general.labels.sms")} - -
+ menu={ + { + const Template = TemplateList("job").appointment_reminder; + GenerateDocument( + { + name: Template.key, + variables: { id: event.job.id }, + }, + { + to: event.job && event.job.ownr_ea, + subject: Template.subject, + }, + "e", + event.job && event.job.id + ); + }, + }, + { + key: "sms", + label: t("general.labels.sms"), + disabled: event.arrived || !bodyshop.messagingservicesid, + onClick: () => { + const p = parsePhoneNumber(event.job.ownr_ph1, "CA"); + if (p && p.isValid()) { + openChatByPhone({ + phone_num: p.formatInternational(), + jobid: event.job.id, + }); + setMessage( + t("appointments.labels.reminder", { + shopname: bodyshop.shopname, + date: dayjs(event.start).format("MM/DD/YYYY"), + time: dayjs(event.start).format("HH:mm a"), + }) + ); + setVisible(false); + } else { + notification["error"]({ + message: t("messaging.error.invalidphone"), + }); + } + }, + }, + ]} + /> } > diff --git a/client/src/components/job-detail-cards/job-detail-cards.dates.component.jsx b/client/src/components/job-detail-cards/job-detail-cards.dates.component.jsx index 0dcc620ef..973eec6f2 100644 --- a/client/src/components/job-detail-cards/job-detail-cards.dates.component.jsx +++ b/client/src/components/job-detail-cards/job-detail-cards.dates.component.jsx @@ -1,119 +1,188 @@ -import { Timeline } from "antd"; +import {Timeline} from "antd"; import React from "react"; -import { useTranslation } from "react-i18next"; -import { DateTimeFormatter } from "../../utils/DateFormatter"; +import {useTranslation} from "react-i18next"; +import {DateTimeFormatter} from "../../utils/DateFormatter"; import CardTemplate from "./job-detail-cards.template.component"; -export default function JobDetailCardsDatesComponent({ loading, data }) { - const { t } = useTranslation(); +export default function JobDetailCardsDatesComponent({loading, data}) { + const {t} = useTranslation(); - return ( - - {data ? ( - - {!( - data.actual_in || - data.scheduled_completion || - data.scheduled_in || - data.actual_completion || - data.scheduled_delivery || - data.actual_delivery || - data.date_estimated || - data.date_open || - data.date_scheduled || - data.date_invoiced || - data.date_exported - ) ? ( -
{t("jobs.errors.nodates")}
- ) : null} - {data.date_last_contacted ? ( - - - {data.date_last_contacted} - - ) : null} - {data.date_open ? ( - - - {data.date_open} - - ) : null} - - {data.date_estimated ? ( - - - {data.date_estimated} - - ) : null} - - {data.date_scheduled ? ( - - - {data.date_scheduled} - - ) : null} - - {data.scheduled_in ? ( - - - {data.scheduled_in} - - ) : null} - {data.actual_in ? ( - - - {data.actual_in} - - ) : null} - {data.date_repairstarted ? ( - - - {data.date_repairstarted} - - ) : null} - {data.scheduled_completion ? ( - - - {data.scheduled_completion} - - ) : null} - - {data.actual_completion ? ( - - - {data.actual_completion} - - ) : null} - - {data.scheduled_delivery ? ( - - - {data.scheduled_delivery} - - ) : null} - - {data.actual_delivery ? ( - - - {data.actual_delivery} - - ) : null} - - {data.date_invoiced ? ( - - - {data.date_invoiced} - - ) : null} - - {data.date_exported ? ( - - - {data.date_exported} - - ) : null} -
- ) : null} -
- ); + return ( + + {data ? ( + + + {data.date_last_contacted} + + ), + }, + ] + : []), + ...(data.date_open + ? [ + { + key: "date_open", + label: ( + <> + + {data.date_open} + + ), + }, + ] + : []), + ...(data.date_estimated + ? [ + { + key: "date_estimated", + label: ( + <> + + {data.date_estimated} + + ), + }, + ] + : []), + ...(data.date_scheduled + ? [ + { + key: "date_scheduled", + label: ( + <> + + {data.date_scheduled} + + ), + }, + ] + : []), + ...(data.scheduled_in + ? [ + { + key: "scheduled_in", + label: ( + <> + + {data.scheduled_in} + + ), + }, + ] + : []), + ...(data.actual_in + ? [ + { + key: "actual_in", + label: ( + <> + + {data.actual_in} + + ), + }, + ] + : []), + ...(data.date_repairstarted + ? [ + { + key: "date_repairstarted", + label: ( + <> + + {data.date_repairstarted} + + ), + }, + ] + : []), + ...(data.scheduled_completion + ? [ + { + key: "scheduled_completion", + label: ( + <> + + {data.scheduled_completion} + + ), + }, + ] + : []), + ...(data.actual_completion + ? [ + { + key: "actual_completion", + label: ( + <> + + {data.actual_completion} + + ), + }, + ] + : []), + ...(data.scheduled_delivery + ? [ + { + key: "scheduled_delivery", + label: ( + <> + + {data.scheduled_delivery} + + ), + }, + ] + : []), + ...(data.actual_delivery + ? [ + { + key: "actual_delivery", + label: ( + <> + + {data.actual_delivery} + + ), + }, + ] + : []), + ...(data.date_invoiced + ? [ + { + key: "date_invoiced", + label: ( + <> + + {data.date_invoiced} + + ), + }, + ] + : []), + ...(data.date_exported + ? [ + { + key: "date_exported", + label: ( + <> + + {data.date_exported} + + ), + }, + ] + : []), + ]} + />) : null} + + ); } diff --git a/client/src/components/job-detail-lines/job-lines-expander.component.jsx b/client/src/components/job-detail-lines/job-lines-expander.component.jsx index 06e00c729..51b70e3eb 100644 --- a/client/src/components/job-detail-lines/job-lines-expander.component.jsx +++ b/client/src/components/job-detail-lines/job-lines-expander.component.jsx @@ -1,94 +1,95 @@ -import { useQuery } from "@apollo/client"; -import { Col, Divider, Row, Skeleton, Space, Timeline, Typography } from "antd"; +import {useQuery} from "@apollo/client"; +import {Col, Divider, Row, Skeleton, Space, Timeline, Typography} from "antd"; import React from "react"; -import { useTranslation } from "react-i18next"; -import { Link } from "react-router-dom"; -import { GET_JOB_LINE_ORDERS } from "../../graphql/jobs.queries"; +import {useTranslation} from "react-i18next"; +import {Link} from "react-router-dom"; +import {GET_JOB_LINE_ORDERS} from "../../graphql/jobs.queries"; import CurrencyFormatter from "../../utils/CurrencyFormatter"; -import { DateFormatter } from "../../utils/DateFormatter"; +import {DateFormatter} from "../../utils/DateFormatter"; import AlertComponent from "../alert/alert.component"; -export default function JobLinesExpander({ jobline, jobid }) { - const { t } = useTranslation(); - const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, { - fetchPolicy: "network-only", - nextFetchPolicy: "network-only", - variables: { - joblineid: jobline.id, - }, - }); +export default function JobLinesExpander({jobline, jobid}) { + const {t} = useTranslation(); + const {loading, error, data} = useQuery(GET_JOB_LINE_ORDERS, { + fetchPolicy: "network-only", + nextFetchPolicy: "network-only", + variables: { + joblineid: jobline.id, + }, + }); - if (loading) return ; - if (error) return ; + if (loading) return ; + if (error) return ; - return ( - - - - {t("parts_orders.labels.parts_orders")} - - - {data.parts_order_lines.length > 0 ? ( - data.parts_order_lines.map((line) => ( - - } wrap> - - {line.parts_order.order_number} - - {line.parts_order.order_date} - {line.parts_order.vendor.name} - - - )) - ) : ( - - {t("parts_orders.labels.notyetordered")} - - )} - - - - {t("bills.labels.bills")} - - {data.billlines.length > 0 ? ( - data.billlines.map((line) => ( - - - - - {line.bill.invoice_number} - - - - - {`${t("billlines.fields.actual_price")}: `} - {line.actual_price} - - - - - {`${t("billlines.fields.actual_cost")}: `} - {line.actual_cost} - - - - {line.bill.date} - - {line.bill.vendor.name} - - - )) - ) : ( - - {t("parts_orders.labels.notyetordered")} - - )} - - - - ); + return ( + + + + {t("parts_orders.labels.parts_orders")} + + 0 + ? data.parts_order_lines.map((line) => ({ + key: line.id, + label: ( + } wrap> + + {line.parts_order.order_number} + + {line.parts_order.order_date} + {line.parts_order.vendor.name} + + ), + })) + : [ + { + key: "no-orders", + label: t("parts_orders.labels.notyetordered"), + }, + ] + } + /> + + {t("bills.labels.bills")} + 0 + ? data.billlines.map((line) => ({ + key: line.id, + label: ( + + + + {line.bill.invoice_number} + + + + {`${t("billlines.fields.actual_price")}: `}{line.actual_price} + + + {`${t("billlines.fields.actual_cost")}: `}{line.actual_cost} + + + {line.bill.date} + + {line.bill.vendor.name} + + ), + })) + : [ + { + key: "no-orders", + label: t("parts_orders.labels.notyetordered"), + }, + ] + } + /> + + + ); } diff --git a/client/src/components/job-detail-lines/job-lines.component.jsx b/client/src/components/job-detail-lines/job-lines.component.jsx index 19015729c..c34402cd6 100644 --- a/client/src/components/job-detail-lines/job-lines.component.jsx +++ b/client/src/components/job-detail-lines/job-lines.component.jsx @@ -411,14 +411,17 @@ export function JobLinesComponent({ }; const markMenu = ( - - {t("joblines.fields.part_types.PAA")} - {t("joblines.fields.part_types.PAN")} - {t("joblines.fields.part_types.PAL")} - {t("joblines.fields.part_types.PAS")} - - {t("general.labels.clear")} - + , disabled: true }, + { key: "clear", label: t("general.labels.clear") }, + ]} + /> ); return ( @@ -549,7 +552,7 @@ export function JobLinesComponent({ > {t("jobs.actions.filterpartsonly")} - +