diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 56e402f25..d37bdbacf 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -1243,6 +1243,69 @@ messages + + admin_jobmarkexported + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + admin_jobmarkforreexport + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + admin_jobunvoid + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + billposted false @@ -5736,6 +5799,27 @@ + + list-ready + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + partsqueue false @@ -7629,6 +7713,27 @@ + + additional_board_statuses + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + color false @@ -8028,6 +8133,27 @@ + + ready_statuses + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + @@ -15862,6 +15988,27 @@ + + refresh + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + required false @@ -23147,6 +23294,27 @@ + + partsstatus + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + pas false @@ -23299,6 +23467,27 @@ + + queued_for_parts + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + rate_ats false @@ -29640,6 +29829,27 @@ + + readyjobs + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + recent false @@ -36665,6 +36875,27 @@ labels + + actual_in + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + alert false @@ -37116,6 +37347,27 @@ + + partsstatus + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + production_note false @@ -41811,6 +42063,27 @@ + + jobs-ready + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + owner-detail false @@ -42821,6 +43094,27 @@ + + readyjobs + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + resetpassword false diff --git a/client/package.json b/client/package.json index 5f86ceec6..5c05e9556 100644 --- a/client/package.json +++ b/client/package.json @@ -10,7 +10,7 @@ "@fingerprintjs/fingerprintjs": "^3.3.3", "@sentry/react": "^6.19.6", "@sentry/tracing": "^6.19.6", - "@splitsoftware/splitio-react": "^1.3.1-rc.1", + "@splitsoftware/splitio-react": "^1.4.0", "@stripe/react-stripe-js": "^1.7.1", "@stripe/stripe-js": "^1.27.0", "@tanem/react-nprogress": "^4.0.12", @@ -23,7 +23,7 @@ "enquire-js": "^0.2.1", "env-cmd": "^10.1.0", "exifr": "^7.1.3", - "firebase": "^9.6.10", + "firebase": "^9.6.11", "graphql": "^16.3.0", "i18next": "^21.6.16", "i18next-browser-languagedetector": "^6.1.4", @@ -31,7 +31,7 @@ "jsreport-browser-client-dist": "^1.3.0", "libphonenumber-js": "^1.9.51", "logrocket": "^2.2.1", - "markerjs2": "^2.21.0", + "markerjs2": "^2.21.1", "moment-business-days": "^1.2.0", "moment-timezone": "^0.5.34", "phone": "^3.1.15", @@ -48,7 +48,7 @@ "react-drag-listview": "^0.1.9", "react-grid-gallery": "^0.5.5", "react-grid-layout": "^1.3.4", - "react-i18next": "^11.16.5", + "react-i18next": "^11.16.6", "react-icons": "^4.3.1", "react-number-format": "^4.9.1", "react-redux": "^7.2.8", diff --git a/client/src/assets/banner4.jpg b/client/src/assets/banner4.jpg new file mode 100755 index 000000000..74c63656f Binary files /dev/null and b/client/src/assets/banner4.jpg differ diff --git a/client/src/components/header/header.component.jsx b/client/src/components/header/header.component.jsx index 46cdd6063..2a4e9698e 100644 --- a/client/src/components/header/header.component.jsx +++ b/client/src/components/header/header.component.jsx @@ -3,6 +3,7 @@ import Icon, { BarChartOutlined, CarFilled, ClockCircleFilled, + CheckCircleOutlined, DashboardFilled, DollarCircleFilled, ExportOutlined, @@ -108,6 +109,9 @@ function Header({ }> {t("menus.header.activejobs")} + }> + {t("menus.header.readyjobs")} + }> {t("menus.header.parts-queue")} diff --git a/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx b/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx new file mode 100644 index 000000000..9ead0530c --- /dev/null +++ b/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx @@ -0,0 +1,80 @@ +import React, { useMemo } from "react"; +import { Row, Col, Tag, Tooltip } from "antd"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop, +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); +export default connect(mapStateToProps, mapDispatchToProps)(JobPartsQueueCount); + +export function JobPartsQueueCount({ bodyshop, parts }) { + const partsStatus = useMemo(() => { + if (!parts) return null; + return parts.reduce( + (acc, val) => { + if (val.part_type === "PAS" || val.part_type === "PASL") return acc; + acc.total = acc.total + val.count; + acc[val.status] = acc[val.status] + val.count; + + return acc; + }, + { + total: 0, + null: 0, + [bodyshop.md_order_statuses.default_bo]: 0, + [bodyshop.md_order_statuses.default_ordered]: 0, + [bodyshop.md_order_statuses.default_received]: 0, + [bodyshop.md_order_statuses.default_returned]: 0, + } + ); + }, [bodyshop, parts]); + + if (!parts) return null; + + return ( + + + + {partsStatus.total} + + + + + {partsStatus["null"]} + + + + + + {partsStatus[bodyshop.md_order_statuses.default_ordered]} + + + + + + + {partsStatus[bodyshop.md_order_statuses.default_received]} + + + + + + + {partsStatus[bodyshop.md_order_statuses.default_returned]} + + + + + + + {partsStatus[bodyshop.md_order_statuses.default_bo]} + + + + + ); +} diff --git a/client/src/components/job-remove-from-parst-queue/job-remove-from-parts-queue.component.jsx b/client/src/components/job-remove-from-parst-queue/job-remove-from-parts-queue.component.jsx index 254f2edd7..a0c95fad8 100644 --- a/client/src/components/job-remove-from-parst-queue/job-remove-from-parts-queue.component.jsx +++ b/client/src/components/job-remove-from-parst-queue/job-remove-from-parts-queue.component.jsx @@ -1,24 +1,23 @@ -import { Button, notification } from "antd"; -import React, { useState } from "react"; import { useMutation } from "@apollo/client"; -import { UPDATE_JOB } from "../../graphql/jobs.queries"; +import { Checkbox, notification, Space, Spin } from "antd"; +import React, { useState } from "react"; import { useTranslation } from "react-i18next"; +import { UPDATE_JOB } from "../../graphql/jobs.queries"; -export default function JobRemoveFromPartsQueue({ jobId, refetch }) { +export default function JobRemoveFromPartsQueue({ checked, jobId }) { const [updateJob] = useMutation(UPDATE_JOB); const { t } = useTranslation(); const [loading, setLoading] = useState(false); - const handleClick = async (e) => { + const handleChange = async (e) => { setLoading(true); const result = await updateJob({ - variables: { jobId: jobId, job: { queued_for_parts: false } }, + variables: { jobId: jobId, job: { queued_for_parts: e.target.checked } }, }); if (!!!result.errors) { notification["success"]({ message: t("jobs.successes.save") }); - if (refetch) refetch(); } else { notification["error"]({ message: t("jobs.errors.saving", { @@ -30,8 +29,9 @@ export default function JobRemoveFromPartsQueue({ jobId, refetch }) { }; return ( - + + + {loading && } + ); } diff --git a/client/src/components/jobs-admin-change-status/jobs-admin-change.status.component.jsx b/client/src/components/jobs-admin-change-status/jobs-admin-change.status.component.jsx index a20c6ea20..edef81343 100644 --- a/client/src/components/jobs-admin-change-status/jobs-admin-change.status.component.jsx +++ b/client/src/components/jobs-admin-change-status/jobs-admin-change.status.component.jsx @@ -6,17 +6,20 @@ import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { UPDATE_JOB_STATUS } from "../../graphql/jobs.queries"; +import { insertAuditTrail } from "../../redux/application/application.actions"; import { selectBodyshop } from "../../redux/user/user.selectors"; +import AuditTrailMapping from "../../utils/AuditTrailMappings"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, }); const mapDispatchToProps = (dispatch) => ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) + insertAuditTrail: ({ jobid, operation }) => + dispatch(insertAuditTrail({ jobid, operation })), }); export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminStatus); -export function JobsAdminStatus({ bodyshop, job }) { +export function JobsAdminStatus({ insertAuditTrail, bodyshop, job }) { const { t } = useTranslation(); const [mutationUpdateJobstatus] = useMutation(UPDATE_JOB_STATUS); @@ -26,6 +29,10 @@ export function JobsAdminStatus({ bodyshop, job }) { }) .then((r) => { notification["success"]({ message: t("jobs.successes.save") }); + insertAuditTrail({ + jobid: job.id, + operation: AuditTrailMapping.admin_jobstatuschange(status), + }); // refetch(); }) .catch((error) => { diff --git a/client/src/components/jobs-admin-dates/jobs-admin-dates.component.jsx b/client/src/components/jobs-admin-dates/jobs-admin-dates.component.jsx index 28cc4595e..309c433cf 100644 --- a/client/src/components/jobs-admin-dates/jobs-admin-dates.component.jsx +++ b/client/src/components/jobs-admin-dates/jobs-admin-dates.component.jsx @@ -7,8 +7,27 @@ import DateTimePicker from "../form-date-time-picker/form-date-time-picker.compo import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import moment from "moment"; import FormDatePicker from "../form-date-picker/form-date-picker.component"; +import AuditTrailMapping from "../../utils/AuditTrailMappings"; -export default function JobsAdminDatesChange({ job }) { +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { insertAuditTrail } from "../../redux/application/application.actions"; + +const mapStateToProps = createStructuredSelector({ + //currentUser: selectCurrentUser +}); + +const mapDispatchToProps = (dispatch) => ({ + insertAuditTrail: ({ jobid, operation }) => + dispatch(insertAuditTrail({ jobid, operation })), +}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(JobsAdminDatesChange); + +export function JobsAdminDatesChange({ insertAuditTrail, job }) { const { t } = useTranslation(); const [loading, setLoading] = useState(false); const [form] = Form.useForm(); @@ -20,6 +39,23 @@ export default function JobsAdminDatesChange({ job }) { variables: { jobId: job.id, job: values }, }); + const changedAuditFields = form.getFieldsValue( + true, + (meta) => meta && meta.touched + ); + + Object.keys(changedAuditFields).forEach((key) => { + insertAuditTrail({ + jobid: job.id, + operation: AuditTrailMapping.admin_jobfieldchange( + key, + changedAuditFields[key] instanceof moment + ? moment(changedAuditFields[key]).format("MM/DD/YYYY hh:mm a") + : changedAuditFields[key] + ), + }); + }); + if (!!!result.errors) { notification["success"]({ message: t("jobs.successes.save") }); } else { diff --git a/client/src/components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component.jsx b/client/src/components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component.jsx index 18b624606..929c7f6d5 100644 --- a/client/src/components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component.jsx +++ b/client/src/components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component.jsx @@ -8,18 +8,21 @@ import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; import moment from "moment"; +import AuditTrailMapping from "../../utils/AuditTrailMappings"; +import { insertAuditTrail } from "../../redux/application/application.actions"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, }); const mapDispatchToProps = (dispatch) => ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) + insertAuditTrail: ({ jobid, operation }) => + dispatch(insertAuditTrail({ jobid, operation })), }); export default connect( mapStateToProps, mapDispatchToProps )(JobAdminMarkReexport); -export function JobAdminMarkReexport({ bodyshop, job }) { +export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, job }) { const { t } = useTranslation(); const [loading, setLoading] = useState(false); const [markJobForReexport] = useMutation(gql` @@ -78,6 +81,10 @@ export function JobAdminMarkReexport({ bodyshop, job }) { if (!result.errors) { notification["success"]({ message: t("jobs.successes.save") }); + insertAuditTrail({ + jobid: job.id, + operation: AuditTrailMapping.admin_jobmarkforreexport(), + }); } else { notification["error"]({ message: t("jobs.errors.saving", { @@ -96,6 +103,10 @@ export function JobAdminMarkReexport({ bodyshop, job }) { if (!result.errors) { notification["success"]({ message: t("jobs.successes.save") }); + insertAuditTrail({ + jobid: job.id, + operation: AuditTrailMapping.admin_jobmarkexported(), + }); } else { notification["error"]({ message: t("jobs.errors.saving", { diff --git a/client/src/components/jobs-admin-unvoid/jobs-admin-unvoid.component.jsx b/client/src/components/jobs-admin-unvoid/jobs-admin-unvoid.component.jsx index 4f36cd850..8682cd406 100644 --- a/client/src/components/jobs-admin-unvoid/jobs-admin-unvoid.component.jsx +++ b/client/src/components/jobs-admin-unvoid/jobs-admin-unvoid.component.jsx @@ -4,21 +4,29 @@ import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; +import { insertAuditTrail } from "../../redux/application/application.actions"; import { selectBodyshop, selectCurrentUser, } from "../../redux/user/user.selectors"; +import AuditTrailMapping from "../../utils/AuditTrailMappings"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, currentUser: selectCurrentUser, }); const mapDispatchToProps = (dispatch) => ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) + insertAuditTrail: ({ jobid, operation }) => + dispatch(insertAuditTrail({ jobid, operation })), }); export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminUnvoid); -export function JobsAdminUnvoid({ bodyshop, job, currentUser }) { +export function JobsAdminUnvoid({ + insertAuditTrail, + bodyshop, + job, + currentUser, +}) { const { t } = useTranslation(); const [loading, setLoading] = useState(false); const [updateJob] = useMutation(gql` @@ -84,6 +92,11 @@ mutation UNVOID_JOB($jobId: uuid!) { if (!result.errors) { notification["success"]({ message: t("jobs.successes.save") }); + + insertAuditTrail({ + jobid: job.id, + operation: AuditTrailMapping.admin_unvoicejob(), + }); } else { notification["error"]({ message: t("jobs.errors.saving", { diff --git a/client/src/components/jobs-ready-list/jobs-ready-list.component.jsx b/client/src/components/jobs-ready-list/jobs-ready-list.component.jsx new file mode 100644 index 000000000..e1a598562 --- /dev/null +++ b/client/src/components/jobs-ready-list/jobs-ready-list.component.jsx @@ -0,0 +1,355 @@ +import { + ExclamationCircleFilled, + PauseCircleOutlined, + SyncOutlined, +} from "@ant-design/icons"; +import { useQuery } from "@apollo/client"; +import { Button, Card, Grid, Input, Space, Table } from "antd"; +import queryString from "query-string"; +import React, { useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { connect } from "react-redux"; +import { Link, useHistory, useLocation } from "react-router-dom"; +import { createStructuredSelector } from "reselect"; +import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +import { onlyUnique } from "../../utils/arrayHelper"; +import CurrencyFormatter from "../../utils/CurrencyFormatter"; +import { alphaSort } from "../../utils/sorters"; +import AlertComponent from "../alert/alert.component"; +import ChatOpenButton from "../chat-open-button/chat-open-button.component"; +import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; + +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop, +}); + +export function JobsReadyList({ bodyshop }) { + const searchParams = queryString.parse(useLocation().search); + const { selected } = searchParams; + const selectedBreakpoint = Object.entries(Grid.useBreakpoint()) + .filter((screen) => !!screen[1]) + .slice(-1)[0]; + + const readyStatuses = useMemo(() => { + if (bodyshop.md_ro_statuses.ready_statuses) + return bodyshop.md_ro_statuses.ready_statuses; + + return bodyshop.md_ro_statuses.post_production_statuses.filter( + (s) => + s !== bodyshop.md_ro_statuses.default_invoiced && + s !== bodyshop.md_ro_statuses.default_exported + ); + }, [bodyshop.md_ro_statuses]); + + const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, { + variables: { + statuses: readyStatuses, + }, + fetchPolicy: "network-only", + nextFetchPolicy: "network-only", + }); + + const [state, setState] = useState({ + sortedInfo: {}, + filteredInfo: { text: "" }, + }); + + const { t } = useTranslation(); + const history = useHistory(); + const [searchText, setSearchText] = useState(""); + + if (error) return ; + + const jobs = data + ? searchText === "" + ? data.jobs + : data.jobs.filter( + (j) => + (j.ro_number || "") + .toString() + .toLowerCase() + .includes(searchText.toLowerCase()) || + (j.ownr_co_nm || "") + .toLowerCase() + .includes(searchText.toLowerCase()) || + (j.comments || "") + .toLowerCase() + .includes(searchText.toLowerCase()) || + (j.ownr_fn || "") + .toLowerCase() + .includes(searchText.toLowerCase()) || + (j.ownr_ln || "") + .toLowerCase() + .includes(searchText.toLowerCase()) || + (j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) || + (j.plate_no || "") + .toLowerCase() + .includes(searchText.toLowerCase()) || + (j.v_model_desc || "") + .toLowerCase() + .includes(searchText.toLowerCase()) || + (j.v_make_desc || "") + .toLowerCase() + .includes(searchText.toLowerCase()) + ) + : []; + + const handleTableChange = (pagination, filters, sorter) => { + setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); + }; + + const handleOnRowClick = (record) => { + if (record) { + if (record.id) { + history.push({ + search: queryString.stringify({ + ...searchParams, + selected: record.id, + }), + }); + } + } + }; + + const columns = [ + { + title: t("jobs.fields.ro_number"), + dataIndex: "ro_number", + key: "ro_number", + sorter: (a, b) => alphaSort(a.ro_number, b.ro_number), + sortOrder: + state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order, + + render: (text, record) => ( + e.stopPropagation()} + > + + {record.ro_number || t("general.labels.na")} + {record.production_vars && record.production_vars.alert ? ( + + ) : null} + {record.suspended && ( + + )} + + + ), + }, + { + title: t("jobs.fields.owner"), + dataIndex: "owner", + key: "owner", + ellipsis: true, + + responsive: ["md"], + sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln), + sortOrder: + state.sortedInfo.columnKey === "owner" && state.sortedInfo.order, + render: (text, record) => { + return record.owner ? ( + e.stopPropagation()} + > + + + ) : ( + + + + ); + }, + }, + { + title: t("jobs.fields.ownr_ph1"), + dataIndex: "ownr_ph1", + key: "ownr_ph1", + ellipsis: true, + responsive: ["md"], + render: (text, record) => ( + + ), + }, + { + title: t("jobs.fields.ownr_ph2"), + dataIndex: "ownr_ph2", + key: "ownr_ph2", + ellipsis: true, + responsive: ["md"], + render: (text, record) => ( + + ), + }, + + { + title: t("jobs.fields.status"), + dataIndex: "status", + key: "status", + ellipsis: true, + + sorter: (a, b) => alphaSort(a.status, b.status), + sortOrder: + state.sortedInfo.columnKey === "status" && state.sortedInfo.order, + filters: + (jobs && + jobs + .map((j) => j.status) + .filter(onlyUnique) + .map((s) => { + return { + text: s || "No Status*", + value: [s], + }; + })) || + [], + onFilter: (value, record) => value.includes(record.status), + }, + + { + title: t("jobs.fields.vehicle"), + dataIndex: "vehicle", + key: "vehicle", + ellipsis: true, + render: (text, record) => { + return record.vehicleid ? ( + e.stopPropagation()} + > + {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ + record.v_model_desc || "" + }`} + + ) : ( + {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ + record.v_model_desc || "" + }`} + ); + }, + }, + { + title: t("vehicles.fields.plate_no"), + dataIndex: "plate_no", + key: "plate_no", + ellipsis: true, + + responsive: ["md"], + sorter: (a, b) => alphaSort(a.plate_no, b.plate_no), + sortOrder: + state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order, + }, + { + title: t("jobs.fields.clm_no"), + dataIndex: "clm_no", + key: "clm_no", + ellipsis: true, + responsive: ["md"], + sorter: (a, b) => alphaSort(a.clm_no, b.clm_no), + sortOrder: + state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order, + render: (text, record) => + `${record.clm_no || ""}${ + record.po_number ? ` (PO: ${record.po_number})` : "" + }`, + }, + { + title: t("jobs.fields.ins_co_nm"), + dataIndex: "ins_co_nm", + key: "ins_co_nm", + ellipsis: true, + responsive: ["md"], + }, + { + title: t("jobs.fields.clm_total"), + dataIndex: "clm_total", + key: "clm_total", + responsive: ["md"], + ellipsis: true, + + sorter: (a, b) => a.clm_total - b.clm_total, + sortOrder: + state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order, + render: (text, record) => ( + {record.clm_total} + ), + }, + { + title: t("jobs.fields.comment"), + dataIndex: "comment", + key: "comment", + ellipsis: true, + responsive: ["md"], + }, + // { + // title: t("jobs.fields.owner_owing"), + // dataIndex: "owner_owing", + // key: "owner_owing", + // responsive: ["md"], + // render: (text, record) => ( + // {record.owner_owing} + // ), + // }, + ]; + + const scrollMapper = { + xs: true, + sm: true, + md: true, + lg: "100%", + xl: "100%", + xxl: "100%", + }; + + return ( + + ({readyStatuses && readyStatuses.join(", ")}) + + { + setSearchText(e.target.value); + }} + value={searchText} + enterButton + /> + + } + > + { + handleOnRowClick(record); + }, + selectedRowKeys: [selected], + type: "radio", + }} + onChange={handleTableChange} + onRow={(record, rowIndex) => { + return { + onClick: (event) => { + handleOnRowClick(record); + }, + }; + }} + /> + + ); +} + +export default connect(mapStateToProps, null)(JobsReadyList); diff --git a/client/src/components/production-board-kanban-card/production-board-kanban-card.component.jsx b/client/src/components/production-board-kanban-card/production-board-kanban-card.component.jsx index 0f56e25f3..e03279d22 100644 --- a/client/src/components/production-board-kanban-card/production-board-kanban-card.component.jsx +++ b/client/src/components/production-board-kanban-card/production-board-kanban-card.component.jsx @@ -1,6 +1,7 @@ import { CalendarOutlined, EyeFilled, + DownloadOutlined, PauseCircleOutlined, } from "@ant-design/icons"; import { Card, Col, Row, Space } from "antd"; @@ -14,6 +15,7 @@ import ProductionSubletsManageComponent from "../production-sublets-manage/produ import "./production-board-card.styles.scss"; import moment from "moment"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; +import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component"; export default function ProductionBoardCard( technician, @@ -157,6 +159,16 @@ export default function ProductionBoardCard( )} */} + {cardSettings && cardSettings.actual_in && card.actual_in && ( + + + + + {card.actual_in} + + + + )} {cardSettings && cardSettings.scheduled_completion && card.scheduled_completion && ( @@ -188,6 +200,11 @@ export default function ProductionBoardCard( )} )} + {cardSettings && cardSettings.partsstatus && ( + + + + )} ); diff --git a/client/src/components/production-board-kanban/production-board-kanban.card-settings.component.jsx b/client/src/components/production-board-kanban/production-board-kanban.card-settings.component.jsx index 0f54c60ac..25c77e307 100644 --- a/client/src/components/production-board-kanban/production-board-kanban.card-settings.component.jsx +++ b/client/src/components/production-board-kanban/production-board-kanban.card-settings.component.jsx @@ -97,6 +97,13 @@ export default function ProductionBoardKanbanCardSettings({ > + + + + + + { const boardData = createBoardData( - bodyshop.md_ro_statuses.production_statuses, + [ + ...bodyshop.md_ro_statuses.production_statuses, + ...(bodyshop.md_ro_statuses.additional_board_statuses || []), + ], data, filter ); @@ -61,13 +64,7 @@ export function ProductionBoardKanbanComponent({ }); setBoardLanes(boardData); setIsMoving(false); - }, [ - data, - setBoardLanes, - setIsMoving, - bodyshop.md_ro_statuses.production_statuses, - filter, - ]); + }, [data, setBoardLanes, setIsMoving, bodyshop.md_ro_statuses, filter]); const client = useApolloClient(); diff --git a/client/src/components/production-list-columns/production-list-columns.data.js b/client/src/components/production-list-columns/production-list-columns.data.js index b46dd9f3d..d99f4e5a2 100644 --- a/client/src/components/production-list-columns/production-list-columns.data.js +++ b/client/src/components/production-list-columns/production-list-columns.data.js @@ -23,6 +23,7 @@ import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.co import ProductionListColumnComment from "./production-list-columns.comment.component"; import ProductionListColumnPartsReceived from "./production-list-columns.partsreceived.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; +import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component"; const r = ({ technician, state, activeStatuses, bodyshop }) => { return [ @@ -490,6 +491,14 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => { ), }, + { + title: i18n.t("jobs.fields.partsstatus"), + dataIndex: "partsstatus", + key: "partsstatus", + render: (text, record) => ( + + ), + }, ]; }; export default r; diff --git a/client/src/components/shop-info/shop-info.rbac.component.jsx b/client/src/components/shop-info/shop-info.rbac.component.jsx index 1f6363e3a..3c0f75c88 100644 --- a/client/src/components/shop-info/shop-info.rbac.component.jsx +++ b/client/src/components/shop-info/shop-info.rbac.component.jsx @@ -165,6 +165,18 @@ export default function ShopInfoRbacComponent({ form }) { > + + + + + + + + +
- {typeof dataSource.title.children === 'string' && + {typeof dataSource.title.children === "string" && dataSource.title.children.match(isImg) ? ( img ) : ( @@ -30,13 +30,18 @@ class Banner extends React.PureComponent {
{dataSource.content.children}
- + + + + - ©2021 ImEX Systems used under + ©2022 ImEX Systems used under license. ), diff --git a/client/src/landing/index.jsx b/client/src/landing/index.jsx index bf2417019..4fcd55f19 100644 --- a/client/src/landing/index.jsx +++ b/client/src/landing/index.jsx @@ -4,27 +4,27 @@ import { enquireScreen } from "enquire-js"; import React from "react"; import Banner0 from "./Banner0"; // import Content4 from "./Content4"; -import Content0 from "./Content0"; -import Content1 from "./Content1"; +//import Content0 from "./Content0"; +//import Content1 from "./Content1"; import { Banner00DataSource, // Content40DataSource, - Content00DataSource, - Content10DataSource, + //Content00DataSource, + //Content10DataSource, // Pricing11DataSource, // Content30DataSource, // Content120DataSource, Footer10DataSource, - Nav00DataSource, - Pricing20DataSource, + // Nav00DataSource, + //Pricing20DataSource, } from "./data.source"; // import Pricing1 from "./Pricing1"; // import Content3 from "./Content3"; // import Content12 from "./Content12"; import Footer1 from "./Footer1"; import "./less/antMotionStyle.less"; -import Nav0 from "./Nav0"; -import Pricing2 from "./Pricing2"; +// import Nav0 from "./Nav0"; +//import Pricing2 from "./Pricing2"; let isMobile; enquireScreen((b) => { @@ -62,64 +62,64 @@ export default class Home extends React.Component { render() { const children = [ - , + // , , - ...(process.env.NODE_ENV !== "production" - ? [ - // , - , - , - , - // , - // , - // , - ] - : []), + // ...(process.env.NODE_ENV !== "production" + // ? [ + // // , + // , + // , + // , + // // , + // // , + // // , + // ] + // : []), +