Compare commits

..

18 Commits

Author SHA1 Message Date
Allan Carr
f0c0b5dc45 IO-3576 Fortellis Refetch Make Model
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2026-02-23 15:28:07 -08:00
Allan Carr
a6a621e73f Merged in hotfix/2026-02-19 (pull request #3026)
IO-3570 Strip - from Owner Name in regex
2026-02-19 21:21:52 +00:00
Allan Carr
ee0f2c3293 Merged in feature/IO-3570-Fortellis-Multiple-Veh-Records (pull request #3024)
IO-3570 Strip - from Owner Name in regex
2026-02-19 21:16:13 +00:00
Allan Carr
83a30f1fcd IO-3570 Strip - from Owner Name in regex
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2026-02-19 13:17:15 -08:00
Allan Carr
ba3e831503 Merged in hotfix/2026-02-19 (pull request #3023)
IO-3570 Fortellis Owner Phone Search
2026-02-19 20:37:09 +00:00
Allan Carr
6b87b15e97 IO-3570 Fortellis Owner Phone Search
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2026-02-19 12:36:43 -08:00
Allan Carr
425cdac26e Merged in feature/IO-3570-Fortellis-Multiple-Veh-Records (pull request #3022)
IO-3570 Fortellis Owner Phone Search
2026-02-19 20:36:15 +00:00
Allan Carr
ade8461851 Merged in hotfix/2026-02-19 (pull request #3020)
Hotfix/2026 02 19
2026-02-19 20:07:21 +00:00
Allan Carr
f6c5f85a87 IO-3570 Transwip fix
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2026-02-19 12:04:44 -08:00
Allan Carr
532fa3fb18 Merged in feature/IO-3570-Fortellis-Multiple-Veh-Records (pull request #3018)
Feature/IO-3570 Fortellis Multiple Veh Records
2026-02-19 20:02:45 +00:00
Allan Carr
c7875c7be3 IO-3570 Fix Regex to include numbers
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2026-02-19 11:59:36 -08:00
Allan Carr
78b9b8d260 Merged in hotfix/2026-02-19 (pull request #3016)
Hotfix/2026 02 19
2026-02-19 18:52:41 +00:00
Allan Carr
38fc3285b4 IO-3570 Check if array and then filter
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2026-02-19 10:52:37 -08:00
Allan Carr
9d14ad3167 Merged in feature/IO-3570-Fortellis-Multiple-Veh-Records (pull request #3015)
Feature/IO-3570 Fortellis Multiple Veh Records
2026-02-19 18:50:28 +00:00
Allan Carr
2e53fe8606 IO-3570 Fortellis Multi Veh
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2026-02-19 10:46:50 -08:00
Allan Carr
6317606ce1 Merged in hotfix/2026-02-19 (pull request #3014)
IO-3570 Filter Vehicle Results from VIN Query to records that only have a vehicleVehID
2026-02-19 18:02:40 +00:00
Allan Carr
e599c2b2d6 Merged in feature/IO-3570-Fortellis-Multiple-Veh-Records (pull request #3013)
IO-3570 Filter Vehicle Results from VIN Query to records that only have a vehicleVehID
2026-02-19 17:58:30 +00:00
Allan Carr
2b35090359 IO-3570 Filter Vehicle Results from VIN Query to records that only have a vehicleVehID
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2026-02-19 09:55:37 -08:00
61 changed files with 3017 additions and 2035 deletions

843
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,9 +8,9 @@
"private": true,
"proxy": "http://localhost:4000",
"dependencies": {
"@amplitude/analytics-browser": "^2.35.0",
"@amplitude/analytics-browser": "^2.34.0",
"@ant-design/pro-layout": "^7.22.6",
"@apollo/client": "^4.1.4",
"@apollo/client": "^4.1.3",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0",
@@ -18,35 +18,35 @@
"@emotion/is-prop-valid": "^1.4.0",
"@fingerprintjs/fingerprintjs": "^5.0.1",
"@firebase/analytics": "^0.10.19",
"@firebase/app": "^0.14.8",
"@firebase/app": "^0.14.7",
"@firebase/auth": "^1.12.0",
"@firebase/firestore": "^4.11.0",
"@firebase/firestore": "^4.10.0",
"@firebase/messaging": "^0.12.22",
"@jsreport/browser-client": "^3.1.0",
"@reduxjs/toolkit": "^2.11.2",
"@sentry/cli": "^3.2.0",
"@sentry/react": "^10.39.0",
"@sentry/vite-plugin": "^4.9.1",
"@sentry/cli": "^3.1.0",
"@sentry/react": "^10.38.0",
"@sentry/vite-plugin": "^4.8.0",
"@splitsoftware/splitio-react": "^2.6.1",
"@tanem/react-nprogress": "^5.0.63",
"antd": "^6.3.0",
"@tanem/react-nprogress": "^5.0.58",
"antd": "^6.2.2",
"apollo-link-logger": "^3.0.0",
"autosize": "^6.0.1",
"axios": "^1.13.5",
"axios": "^1.13.4",
"classnames": "^2.5.1",
"css-box-model": "^1.2.1",
"dayjs": "^1.11.19",
"dayjs-business-days2": "^1.3.2",
"dinero.js": "^1.9.1",
"dotenv": "^17.3.1",
"dotenv": "^17.2.3",
"env-cmd": "^11.0.0",
"exifr": "^7.1.3",
"graphql": "^16.12.0",
"graphql-ws": "^6.0.7",
"i18next": "^25.8.11",
"i18next-browser-languagedetector": "^8.2.1",
"i18next": "^25.8.0",
"i18next-browser-languagedetector": "^8.2.0",
"immutability-helper": "^3.1.1",
"libphonenumber-js": "^1.12.37",
"libphonenumber-js": "^1.12.36",
"lightningcss": "^1.31.1",
"logrocket": "^12.0.0",
"markerjs2": "^2.32.7",
@@ -54,7 +54,7 @@
"normalize-url": "^8.1.1",
"object-hash": "^3.0.0",
"phone": "^3.1.70",
"posthog-js": "^1.351.1",
"posthog-js": "^1.336.4",
"prop-types": "^15.8.1",
"query-string": "^9.3.1",
"raf-schd": "^4.0.3",
@@ -87,7 +87,7 @@
"rxjs": "^7.8.2",
"sass": "^1.97.3",
"socket.io-client": "^4.8.3",
"styled-components": "^6.3.10",
"styled-components": "^6.3.8",
"vite-plugin-ejs": "^1.7.0",
"web-vitals": "^5.1.0"
},
@@ -144,11 +144,11 @@
"@emotion/babel-plugin": "^11.13.5",
"@emotion/react": "^11.14.0",
"@eslint/js": "^9.39.2",
"@playwright/test": "^1.58.2",
"@playwright/test": "^1.58.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@vitejs/plugin-react": "^5.1.4",
"@vitejs/plugin-react": "^5.1.2",
"babel-plugin-react-compiler": "^1.0.0",
"browserslist": "^4.28.1",
"browserslist-to-esbuild": "^2.1.1",
@@ -156,16 +156,16 @@
"eslint": "^9.39.2",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
"globals": "^17.3.0",
"jsdom": "^28.1.0",
"globals": "^17.2.0",
"jsdom": "^27.4.0",
"memfs": "^4.56.10",
"os-browserify": "^0.3.0",
"playwright": "^1.58.2",
"playwright": "^1.58.0",
"react-error-overlay": "^6.1.0",
"redux-logger": "^3.0.6",
"source-map-explorer": "^2.5.3",
"vite": "^7.3.1",
"vite-plugin-babel": "^1.5.1",
"vite-plugin-babel": "^1.4.1",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-node-polyfills": "^0.25.0",
"vite-plugin-pwa": "^1.2.0",

View File

@@ -29,18 +29,19 @@ export function AllocationsAssignmentComponent({
<Select
id="employeeSelector"
showSearch={{
optionFilterProp: "label",
filterOption: (input, option) => option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
optionFilterProp: "children",
filterOption: (input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}}
style={{ width: 200 }}
placeholder="Select a person"
onChange={onChange}
options={bodyshop.employees.map((emp) => ({
value: emp.id,
key: emp.id,
label: `${emp.first_name} ${emp.last_name}`
}))}
/>
>
{bodyshop.employees.map((emp) => (
<Select.Option value={emp.id} key={emp.id}>
{`${emp.first_name} ${emp.last_name}`}
</Select.Option>
))}
</Select>
<InputNumber
defaultValue={assignment.hours}
placeholder={t("joblines.fields.mod_lb_hrs")}

View File

@@ -31,17 +31,19 @@ export default connect(
<div>
<Select
showSearch={{
optionFilterProp: "label",
filterOption: (input, option) => option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
optionFilterProp: "children",
filterOption: (input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}}
style={{ width: 200 }}
placeholder="Select a person"
onChange={onChange}
options={bodyshop.employees.map((emp) => ({
value: emp.id,
label: `${emp.first_name} ${emp.last_name}`
}))}
/>
>
{bodyshop.employees.map((emp) => (
<Select.Option value={emp.id} key={emp.id}>
{`${emp.first_name} ${emp.last_name}`}
</Select.Option>
))}
</Select>
<Button type="primary" disabled={!assignment.employeeid} onClick={handleAssignment}>
Assign

View File

@@ -99,22 +99,20 @@ export function BillFormItemsExtendedFormItem({
}}
</Form.Item>
<Form.Item label={t("billlines.fields.cost_center")} name={["billlineskeys", record.id, "cost_center"]}>
<Select
showSearch
style={{ minWidth: "3rem" }}
disabled={disabled}
options={
bodyshopHasDmsKey(bodyshop)
? CiecaSelect(true, false)
: responsibilityCenters.costs.map((item) => ({ value: item.name, label: item.name }))
}
/>
<Select showSearch style={{ minWidth: "3rem" }} disabled={disabled}>
{bodyshopHasDmsKey(bodyshop)
? CiecaSelect(true, false)
: responsibilityCenters.costs.map((item) => <Select.Option key={item.name}>{item.name}</Select.Option>)}
</Select>
</Form.Item>
<Form.Item label={t("billlines.fields.location")} name={["billlineskeys", record.id, "location"]}>
<Select
disabled={disabled}
options={bodyshop.md_parts_locations.map((loc) => ({ value: loc, label: loc }))}
/>
<Select disabled={disabled}>
{bodyshop.md_parts_locations.map((loc, idx) => (
<Select.Option key={idx} value={loc}>
{loc}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("billlines.fields.deductedfromlbr")}
@@ -138,10 +136,22 @@ export function BillFormItemsExtendedFormItem({
]}
name={["billlineskeys", record.id, "lbr_adjustment", "mod_lbr_ty"]}
>
<Select
allowClear
options={CiecaSelect(false, true)}
/>
<Select allowClear>
<Select.Option value="LAA">{t("joblines.fields.lbr_types.LAA")}</Select.Option>
<Select.Option value="LAB">{t("joblines.fields.lbr_types.LAB")}</Select.Option>
<Select.Option value="LAD">{t("joblines.fields.lbr_types.LAD")}</Select.Option>
<Select.Option value="LAE">{t("joblines.fields.lbr_types.LAE")}</Select.Option>
<Select.Option value="LAF">{t("joblines.fields.lbr_types.LAF")}</Select.Option>
<Select.Option value="LAG">{t("joblines.fields.lbr_types.LAG")}</Select.Option>
<Select.Option value="LAM">{t("joblines.fields.lbr_types.LAM")}</Select.Option>
<Select.Option value="LAR">{t("joblines.fields.lbr_types.LAR")}</Select.Option>
<Select.Option value="LAS">{t("joblines.fields.lbr_types.LAS")}</Select.Option>
<Select.Option value="LAU">{t("joblines.fields.lbr_types.LAU")}</Select.Option>
<Select.Option value="LA1">{t("joblines.fields.lbr_types.LA1")}</Select.Option>
<Select.Option value="LA2">{t("joblines.fields.lbr_types.LA2")}</Select.Option>
<Select.Option value="LA3">{t("joblines.fields.lbr_types.LA3")}</Select.Option>
<Select.Option value="LA4">{t("joblines.fields.lbr_types.LA4")}</Select.Option>
</Select>
</Form.Item>
<Form.Item
label={t("jobs.labels.adjustmentrate")}

View File

@@ -328,12 +328,13 @@ export function BillFormComponent({
</Form.Item>
{!billEdit && (
<Form.Item label={t("bills.fields.allpartslocation")} name="location">
<Select
style={{ width: "10rem" }}
disabled={disabled}
allowClear
options={bodyshop.md_parts_locations.map((loc) => ({ value: loc, label: loc }))}
/>
<Select style={{ width: "10rem" }} disabled={disabled} allowClear>
{bodyshop.md_parts_locations.map((loc, idx) => (
<Select.Option key={idx} value={loc}>
{loc}
</Select.Option>
))}
</Select>
</Form.Item>
)}
</LayoutFormRow>

View File

@@ -399,17 +399,11 @@ export function BillEnterModalLinesComponent({
rules: [{ required: true }]
}),
formInput: () => (
<Select
showSearch
style={{ minWidth: "3rem" }}
disabled={disabled}
tabIndex={0}
options={
bodyshopHasDmsKey(bodyshop)
? CiecaSelect(true, false)
: responsibilityCenters.costs.map((item) => ({ value: item.name, label: item.name }))
}
/>
<Select showSearch style={{ minWidth: "3rem" }} disabled={disabled} tabIndex={0}>
{bodyshopHasDmsKey(bodyshop)
? CiecaSelect(true, false)
: responsibilityCenters.costs.map((item) => <Select.Option key={item.name}>{item.name}</Select.Option>)}
</Select>
)
},
...(billEdit
@@ -425,11 +419,13 @@ export function BillEnterModalLinesComponent({
name: [field.name, "location"]
}),
formInput: () => (
<Select
disabled={disabled}
tabIndex={0}
options={bodyshop.md_parts_locations.map((loc) => ({ value: loc, label: loc }))}
/>
<Select disabled={disabled} tabIndex={0}>
{bodyshop.md_parts_locations.map((loc, idx) => (
<Select.Option key={idx} value={loc}>
{loc}
</Select.Option>
))}
</Select>
)
}
]),
@@ -470,10 +466,22 @@ export function BillEnterModalLinesComponent({
rules={[{ required: true }]}
name={[record.name, "lbr_adjustment", "mod_lbr_ty"]}
>
<Select
allowClear
options={CiecaSelect(false, true)}
/>
<Select allowClear>
<Select.Option value="LAA">{t("joblines.fields.lbr_types.LAA")}</Select.Option>
<Select.Option value="LAB">{t("joblines.fields.lbr_types.LAB")}</Select.Option>
<Select.Option value="LAD">{t("joblines.fields.lbr_types.LAD")}</Select.Option>
<Select.Option value="LAE">{t("joblines.fields.lbr_types.LAE")}</Select.Option>
<Select.Option value="LAF">{t("joblines.fields.lbr_types.LAF")}</Select.Option>
<Select.Option value="LAG">{t("joblines.fields.lbr_types.LAG")}</Select.Option>
<Select.Option value="LAM">{t("joblines.fields.lbr_types.LAM")}</Select.Option>
<Select.Option value="LAR">{t("joblines.fields.lbr_types.LAR")}</Select.Option>
<Select.Option value="LAS">{t("joblines.fields.lbr_types.LAS")}</Select.Option>
<Select.Option value="LAU">{t("joblines.fields.lbr_types.LAU")}</Select.Option>
<Select.Option value="LA1">{t("joblines.fields.lbr_types.LA1")}</Select.Option>
<Select.Option value="LA2">{t("joblines.fields.lbr_types.LA2")}</Select.Option>
<Select.Option value="LA3">{t("joblines.fields.lbr_types.LA3")}</Select.Option>
<Select.Option value="LA4">{t("joblines.fields.lbr_types.LA4")}</Select.Option>
</Select>
</Form.Item>
{Enhanced_Payroll.treatment === "on" ? (

View File

@@ -19,12 +19,13 @@ export default function ChatTagRoComponent({ roOptions, loading, handleSearch, h
placeholder={t("general.labels.search")}
onSelect={handleInsertTag}
notFoundContent={loading ? <LoadingOutlined /> : <Empty />}
options={roOptions.map((item, idx) => ({
key: item.id || idx,
value: item.id || idx,
label: ` ${item.ro_number || ""} | ${OwnerNameDisplayFunction(item)}`
}))}
/>
>
{roOptions.map((item, idx) => (
<Select.Option key={item.id || idx}>
{` ${item.ro_number || ""} | ${OwnerNameDisplayFunction(item)}`}
</Select.Option>
))}
</Select>
</div>
{loading ? <LoadingOutlined /> : null}

View File

@@ -309,13 +309,13 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract, disabled
}
]}
>
<Select
options={bodyshop.md_ins_cos.map((s) => ({
key: s.name,
value: s.name,
label: s.name
}))}
/>
<Select>
{bodyshop.md_ins_cos.map((s) => (
<Select.Option key={s.name} value={s.name}>
{s.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name={"class"}
@@ -327,13 +327,13 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract, disabled
}
]}
>
<Select
options={bodyshop.md_classes.map((s) => ({
key: s,
value: s,
label: s
}))}
/>
<Select>
{bodyshop.md_classes.map((s) => (
<Select.Option key={s} value={s}>
{s}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("contracts.labels.convertform.applycleanupcharge")}

View File

@@ -2,6 +2,8 @@ import { useEffect, useState } from "react";
import { Select } from "antd";
import { useTranslation } from "react-i18next";
const { Option } = Select;
const ContractStatusComponent = ({ value, onChange, ref }) => {
const [option, setOption] = useState(value);
const { t } = useTranslation();
@@ -13,17 +15,11 @@ const ContractStatusComponent = ({ value, onChange, ref }) => {
}, [value, option, onChange]);
return (
<Select
ref={ref}
value={option}
style={{ width: 100 }}
onChange={setOption}
options={[
{ value: "contracts.status.new", label: t("contracts.status.new") },
{ value: "contracts.status.out", label: t("contracts.status.out") },
{ value: "contracts.status.returned", label: t("contracts.status.out") }
]}
/>
<Select ref={ref} value={option} style={{ width: 100 }} onChange={setOption}>
<Option value="contracts.status.new">{t("contracts.status.new")}</Option>
<Option value="contracts.status.out">{t("contracts.status.out")}</Option>
<Option value="contracts.status.returned">{t("contracts.status.out")}</Option>
</Select>
);
};

View File

@@ -2,6 +2,8 @@ import { Select } from "antd";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
const { Option } = Select;
const CourtesyCarReadinessComponent = ({ value, onChange, ref }) => {
const [option, setOption] = useState(value);
const { t } = useTranslation();
@@ -21,11 +23,10 @@ const CourtesyCarReadinessComponent = ({ value, onChange, ref }) => {
width: 100
}}
onChange={setOption}
options={[
{ value: "courtesycars.readiness.ready", label: t("courtesycars.readiness.ready") },
{ value: "courtesycars.readiness.notready", label: t("courtesycars.readiness.notready") }
]}
/>
>
<Option value="courtesycars.readiness.ready">{t("courtesycars.readiness.ready")}</Option>
<Option value="courtesycars.readiness.notready">{t("courtesycars.readiness.notready")}</Option>
</Select>
);
};
export default CourtesyCarReadinessComponent;

View File

@@ -2,6 +2,8 @@ import { useEffect, useState } from "react";
import { Select } from "antd";
import { useTranslation } from "react-i18next";
const { Option } = Select;
const CourtesyCarStatusComponent = ({ value, onChange, ref }) => {
const [option, setOption] = useState(value);
const { t } = useTranslation();
@@ -20,15 +22,14 @@ const CourtesyCarStatusComponent = ({ value, onChange, ref }) => {
width: 100
}}
onChange={setOption}
options={[
{ value: "courtesycars.status.in", label: t("courtesycars.status.in") },
{ value: "courtesycars.status.inservice", label: t("courtesycars.status.inservice") },
{ value: "courtesycars.status.out", label: t("courtesycars.status.out") },
{ value: "courtesycars.status.sold", label: t("courtesycars.status.sold") },
{ value: "courtesycars.status.leasereturn", label: t("courtesycars.status.leasereturn") },
{ value: "courtesycars.status.unavailable", label: t("courtesycars.status.unavailable") }
]}
/>
>
<Option value="courtesycars.status.in">{t("courtesycars.status.in")}</Option>
<Option value="courtesycars.status.inservice">{t("courtesycars.status.inservice")}</Option>
<Option value="courtesycars.status.out">{t("courtesycars.status.out")}</Option>
<Option value="courtesycars.status.sold">{t("courtesycars.status.sold")}</Option>
<Option value="courtesycars.status.leasereturn">{t("courtesycars.status.leasereturn")}</Option>
<Option value="courtesycars.status.unavailable">{t("courtesycars.status.unavailable")}</Option>
</Select>
);
};
export default CourtesyCarStatusComponent;

View File

@@ -272,19 +272,11 @@ export default function CdkLikePostForm({ bodyshop, socket, job, logsRef, mode,
name={[field.name, "name"]}
rules={[{ required: true }]}
>
<Select
showSearch={{
optionFilterProp: "label",
filterOption: (input, option) => option.label.toLowerCase().includes(input.toLowerCase())
}}
style={{ width: "100%" }}
onSelect={(value) => handlePayerSelect(value, index)}
options={bodyshop.cdk_configuration?.payers?.map((payer) => ({
key: payer.name,
value: payer.name,
label: payer.name
}))}
/>
<Select style={{ width: "100%" }} onSelect={(value) => handlePayerSelect(value, index)}>
{bodyshop.cdk_configuration?.payers?.map((payer) => (
<Select.Option key={payer.name}>{payer.name}</Select.Option>
))}
</Select>
</Form.Item>
</Col>

View File

@@ -86,13 +86,11 @@ export function EmailOverlayComponent({ emailConfig, form, selectedMediaState, b
}
]}
>
<Select
options={[
{ key: currentUser.email, value: currentUser.email, label: currentUser.email },
{ key: bodyshop.email, value: bodyshop.email, label: bodyshop.email },
...(bodyshop.md_from_emails ? bodyshop.md_from_emails.map((e) => ({ key: e, value: e, label: e })) : [])
]}
/>
<Select>
<Select.Option key={currentUser.email}>{currentUser.email}</Select.Option>
<Select.Option key={bodyshop.email}>{bodyshop.email}</Select.Option>
{bodyshop.md_from_emails && bodyshop.md_from_emails.map((e) => <Select.Option key={e}>{e}</Select.Option>)}
</Select>
</Form.Item>
<Form.Item
label={

View File

@@ -1,6 +1,7 @@
import { Select, Space, Tag } from "antd";
import { useTranslation } from "react-i18next";
const { Option } = Select;
//To be used as a form element only.
const EmployeeSearchSelectEmail = ({ options, ...props }) => {
@@ -11,24 +12,26 @@ const EmployeeSearchSelectEmail = ({ options, ...props }) => {
showSearch={{
optionFilterProp: "search"
}}
// value={option}
style={{
width: 400
}}
options={options?.map((o) => ({
key: o.id,
value: o.user_email,
search: `${o.employee_number} ${o.first_name} ${o.last_name}`,
label: (
<Space>
{`${o.employee_number} ${o.first_name} ${o.last_name}`}
<Tag color="green">
{o.flat_rate ? t("timetickets.labels.flat_rate") : t("timetickets.labels.straight_time")}
</Tag>
</Space>
)
}))}
{...props}
/>
>
{options
? options.map((o) => (
<Option key={o.id} value={o.user_email} search={`${o.employee_number} ${o.first_name} ${o.last_name}`}>
<Space>
{`${o.employee_number} ${o.first_name} ${o.last_name}`}
<Tag color="green">
{o.flat_rate ? t("timetickets.labels.flat_rate") : t("timetickets.labels.straight_time")}
</Tag>
</Space>
</Option>
))
: null}
</Select>
);
};
export default EmployeeSearchSelectEmail;

View File

@@ -1,6 +1,7 @@
import { Select, Space, Tag } from "antd";
import { useTranslation } from "react-i18next";
const { Option } = Select;
//To be used as a form element only.
const EmployeeSearchSelect = ({ options, showEmail, ...props }) => {
@@ -11,29 +12,30 @@ const EmployeeSearchSelect = ({ options, showEmail, ...props }) => {
showSearch={{
optionFilterProp: "search"
}}
// value={option}
style={{
width: 400
}}
options={options?.map((o) => ({
key: o.id,
value: o.id,
search: `${o.employee_number} ${o.first_name} ${o.last_name}`,
label: (
<Space size="small">
{`${o.employee_number ?? ""} ${o.first_name} ${o.last_name}`}
<Tag color="green" style={{ padding: "0.1 0.1rem", marginRight: "1px", marginLeft: "1px" }}>
{o.flat_rate ? t("timetickets.labels.flat_rate") : t("timetickets.labels.straight_time")}
</Tag>
{showEmail && o.user_email ? (
<Tag color="blue" style={{ padding: "0.1 0.1rem", marginRight: "1px", marginLeft: "1px" }}>
{o.user_email}
</Tag>
) : null}
</Space>
)
}))}
{...props}
/>
>
{options
? options.map((o) => (
<Option key={o.id} value={o.id} search={`${o.employee_number} ${o.first_name} ${o.last_name}`}>
<Space size="small">
{`${o.employee_number ?? ""} ${o.first_name} ${o.last_name}`}
<Tag color="green" style={{ padding: "0.1 0.1rem", marginRight: "1px", marginLeft: "1px" }}>
{o.flat_rate ? t("timetickets.labels.flat_rate") : t("timetickets.labels.straight_time")}
</Tag>
{showEmail && o.user_email ? (
<Tag color="blue" style={{ padding: "0.1 0.1rem", marginRight: "1px", marginLeft: "1px" }}>
{o.user_email}
</Tag>
) : null}
</Space>
</Option>
))
: null}
</Select>
);
};
export default EmployeeSearchSelect;

View File

@@ -67,19 +67,16 @@ export function Jobd3RdPartyModal({ bodyshop, jobId, job, technician }) {
);
};
const handleInsSelect = (value) => {
const selectedVendor = bodyshop.md_ins_cos.find(s => s.name === value);
if (selectedVendor) {
form.setFieldsValue({
addr1: selectedVendor.name,
addr2: selectedVendor.street1,
addr3: selectedVendor.street2,
city: selectedVendor.city,
state: selectedVendor.state,
zip: selectedVendor.zip,
vendorid: null
});
}
const handleInsSelect = (value, option) => {
form.setFieldsValue({
addr1: option.obj.name,
addr2: option.obj.street1,
addr3: option.obj.street2,
city: option.obj.city,
state: option.obj.state,
zip: option.obj.zip,
vendorid: null
});
};
const handleVendorSelect = (vendorid) => {
@@ -106,13 +103,13 @@ export function Jobd3RdPartyModal({ bodyshop, jobId, job, technician }) {
<VendorSearchSelect options={VendorAutoCompleteData?.vendors} onSelect={handleVendorSelect} />
</Form.Item>
<Form.Item label={t("bodyshop.fields.md_ins_co.name")} name="ins_co_id">
<Select
onSelect={handleInsSelect}
options={bodyshop.md_ins_cos.map((s) => ({
value: s.name,
label: s.name
}))}
/>
<Select onSelect={handleInsSelect}>
{bodyshop.md_ins_cos.map((s) => (
<Select.Option key={s.name} obj={s} value={s.name}>
{s.name}
</Select.Option>
))}
</Select>
</Form.Item>
<LayoutFormRow grow>
<Form.Item label={t("printcenter.jobs.3rdpartyfields.addr1")} name="addr1">

View File

@@ -88,15 +88,17 @@ export function JoblineBulkAssign({ setSelectedLines, selectedLines, insertAudit
>
<Select
showSearch={{
optionFilterProp: "label",
filterOption: (input, option) => option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
optionFilterProp: "children",
filterOption: (input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}}
style={{ width: 200 }}
options={bodyshop.employee_teams.map((team) => ({
value: team.id,
label: team.name
}))}
/>
>
{bodyshop.employee_teams.map((team) => (
<Select.Option value={team.id} key={team.id} name={team.name}>
{team.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Space wrap>

View File

@@ -122,26 +122,22 @@ export function JobLineConvertToLabor({
}
]}
>
<Select
allowClear
showSearch
options={[
{ value: "LAA", label: t("joblines.fields.lbr_types.LAA") },
{ value: "LAB", label: t("joblines.fields.lbr_types.LAB") },
{ value: "LAD", label: t("joblines.fields.lbr_types.LAD") },
{ value: "LAE", label: t("joblines.fields.lbr_types.LAE") },
{ value: "LAF", label: t("joblines.fields.lbr_types.LAF") },
{ value: "LAG", label: t("joblines.fields.lbr_types.LAG") },
{ value: "LAM", label: t("joblines.fields.lbr_types.LAM") },
{ value: "LAR", label: t("joblines.fields.lbr_types.LAR") },
{ value: "LAS", label: t("joblines.fields.lbr_types.LAS") },
{ value: "LAU", label: t("joblines.fields.lbr_types.LAU") },
{ value: "LA1", label: t("joblines.fields.lbr_types.LA1") },
{ value: "LA2", label: t("joblines.fields.lbr_types.LA2") },
{ value: "LA3", label: t("joblines.fields.lbr_types.LA3") },
{ value: "LA4", label: t("joblines.fields.lbr_types.LA4") }
]}
/>
<Select allowClear showSearch={{ optionFilterProp: "children" }}>
<Select.Option value="LAA">{t("joblines.fields.lbr_types.LAA")}</Select.Option>
<Select.Option value="LAB">{t("joblines.fields.lbr_types.LAB")}</Select.Option>
<Select.Option value="LAD">{t("joblines.fields.lbr_types.LAD")}</Select.Option>
<Select.Option value="LAE">{t("joblines.fields.lbr_types.LAE")}</Select.Option>
<Select.Option value="LAF">{t("joblines.fields.lbr_types.LAF")}</Select.Option>
<Select.Option value="LAG">{t("joblines.fields.lbr_types.LAG")}</Select.Option>
<Select.Option value="LAM">{t("joblines.fields.lbr_types.LAM")}</Select.Option>
<Select.Option value="LAR">{t("joblines.fields.lbr_types.LAR")}</Select.Option>
<Select.Option value="LAS">{t("joblines.fields.lbr_types.LAS")}</Select.Option>
<Select.Option value="LAU">{t("joblines.fields.lbr_types.LAU")}</Select.Option>
<Select.Option value="LA1">{t("joblines.fields.lbr_types.LA1")}</Select.Option>
<Select.Option value="LA2">{t("joblines.fields.lbr_types.LA2")}</Select.Option>
<Select.Option value="LA3">{t("joblines.fields.lbr_types.LA3")}</Select.Option>
<Select.Option value="LA4">{t("joblines.fields.lbr_types.LA4")}</Select.Option>
</Select>
</Form.Item>
<Form.Item shouldUpdate>

View File

@@ -115,18 +115,19 @@ export function JobLineDispatchButton({
>
<Select
showSearch={{
optionFilterProp: "label",
filterOption: (input, option) => option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
optionFilterProp: "children",
filterOption: (input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}}
style={{ width: 200 }}
options={bodyshop.employees
>
{bodyshop.employees
.filter((emp) => emp.active)
.map((emp) => ({
value: emp.id,
key: emp.id,
label: `${emp.first_name} ${emp.last_name}`
}))}
/>
.map((emp) => (
<Select.Option value={emp.id} key={emp.id} name={`${emp.first_name} ${emp.last_name}`}>
{`${emp.first_name} ${emp.last_name}`}
</Select.Option>
))}
</Select>
</Form.Item>
<Space wrap>

View File

@@ -64,12 +64,13 @@ export function JobLineStatusPopup({ bodyshop, jobline, disabled }) {
onSelect={handleChange}
onBlur={handleSave}
onClear={() => handleChange(null)}
options={Object.values(bodyshop.md_order_statuses).map((s, idx) => ({
key: idx,
value: s,
label: s
}))}
/>
>
{Object.values(bodyshop.md_order_statuses).map((s, idx) => (
<Select.Option key={idx} value={s}>
{s}
</Select.Option>
))}
</Select>
</LoadingSpinner>
</div>
);

View File

@@ -75,12 +75,13 @@ export function JoblineTeamAssignment({ bodyshop, jobline, disabled, jobId, inse
onSelect={handleChange}
onBlur={handleSave}
onClear={() => handleChange(null)}
options={Object.values(bodyshop.employee_teams).map((s) => ({
key: s.id,
value: s.id,
label: s.name
}))}
/>
>
{Object.values(bodyshop.employee_teams).map((s, idx) => (
<Select.Option key={idx} value={s.id}>
{s.name}
</Select.Option>
))}
</Select>
</LoadingSpinner>
</div>
);

View File

@@ -67,22 +67,22 @@ export function JobLinesUpsertModalComponent({ bodyshop, open, jobLine, handleCa
</LayoutFormRow>
<LayoutFormRow grow>
<Form.Item label={t("joblines.fields.mod_lbr_ty")} name="mod_lbr_ty">
<Select allowClear options={[
{ value: "LAA", label: t("joblines.fields.lbr_types.LAA") },
{ value: "LAB", label: t("joblines.fields.lbr_types.LAB") },
{ value: "LAD", label: t("joblines.fields.lbr_types.LAD") },
{ value: "LAE", label: t("joblines.fields.lbr_types.LAE") },
{ value: "LAF", label: t("joblines.fields.lbr_types.LAF") },
{ value: "LAG", label: t("joblines.fields.lbr_types.LAG") },
{ value: "LAM", label: t("joblines.fields.lbr_types.LAM") },
{ value: "LAR", label: t("joblines.fields.lbr_types.LAR") },
{ value: "LAS", label: t("joblines.fields.lbr_types.LAS") },
{ value: "LAU", label: t("joblines.fields.lbr_types.LAU") },
{ value: "LA1", label: t("joblines.fields.lbr_types.LA1") },
{ value: "LA2", label: t("joblines.fields.lbr_types.LA2") },
{ value: "LA3", label: t("joblines.fields.lbr_types.LA3") },
{ value: "LA4", label: t("joblines.fields.lbr_types.LA4") }
]} />
<Select allowClear>
<Select.Option value="LAA">{t("joblines.fields.lbr_types.LAA")}</Select.Option>
<Select.Option value="LAB">{t("joblines.fields.lbr_types.LAB")}</Select.Option>
<Select.Option value="LAD">{t("joblines.fields.lbr_types.LAD")}</Select.Option>
<Select.Option value="LAE">{t("joblines.fields.lbr_types.LAE")}</Select.Option>
<Select.Option value="LAF">{t("joblines.fields.lbr_types.LAF")}</Select.Option>
<Select.Option value="LAG">{t("joblines.fields.lbr_types.LAG")}</Select.Option>
<Select.Option value="LAM">{t("joblines.fields.lbr_types.LAM")}</Select.Option>
<Select.Option value="LAR">{t("joblines.fields.lbr_types.LAR")}</Select.Option>
<Select.Option value="LAS">{t("joblines.fields.lbr_types.LAS")}</Select.Option>
<Select.Option value="LAU">{t("joblines.fields.lbr_types.LAU")}</Select.Option>
<Select.Option value="LA1">{t("joblines.fields.lbr_types.LA1")}</Select.Option>
<Select.Option value="LA2">{t("joblines.fields.lbr_types.LA2")}</Select.Option>
<Select.Option value="LA3">{t("joblines.fields.lbr_types.LA3")}</Select.Option>
<Select.Option value="LA4">{t("joblines.fields.lbr_types.LA4")}</Select.Option>
</Select>
</Form.Item>
<Form.Item label={t("joblines.fields.op_code_desc")} name="op_code_desc">
<Input />
@@ -128,17 +128,17 @@ export function JobLinesUpsertModalComponent({ bodyshop, open, jobLine, handleCa
</LayoutFormRow>
<LayoutFormRow>
<Form.Item label={t("joblines.fields.part_type")} name="part_type">
<Select allowClear options={[
{ value: "PAA", label: t("joblines.fields.part_types.PAA") },
{ value: "PAC", label: t("joblines.fields.part_types.PAC") },
{ value: "PAE", label: t("joblines.fields.part_types.PAE") },
{ value: "PAL", label: t("joblines.fields.part_types.PAL") },
{ value: "PAM", label: t("joblines.fields.part_types.PAM") },
{ value: "PAN", label: t("joblines.fields.part_types.PAN") },
{ value: "PAO", label: t("joblines.fields.part_types.PAO") },
{ value: "PAR", label: t("joblines.fields.part_types.PAR") },
{ value: "PAS", label: t("joblines.fields.part_types.PAS") }
]} />
<Select allowClear>
<Select.Option value="PAA">{t("joblines.fields.part_types.PAA")}</Select.Option>
<Select.Option value="PAC">{t("joblines.fields.part_types.PAC")}</Select.Option>
<Select.Option value="PAE">{t("joblines.fields.part_types.PAE")}</Select.Option>
<Select.Option value="PAL">{t("joblines.fields.part_types.PAL")}</Select.Option>
<Select.Option value="PAM">{t("joblines.fields.part_types.PAM")}</Select.Option>
<Select.Option value="PAN">{t("joblines.fields.part_types.PAN")}</Select.Option>
<Select.Option value="PAO">{t("joblines.fields.part_types.PAO")}</Select.Option>
<Select.Option value="PAR">{t("joblines.fields.part_types.PAR")}</Select.Option>
<Select.Option value="PAS">{t("joblines.fields.part_types.PAS")}</Select.Option>
</Select>
</Form.Item>
<Form.Item label={t("joblines.fields.oem_partno")} name="oem_partno">
<Input />

View File

@@ -8,6 +8,8 @@ import { SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE, SEARCH_JOBS_FOR_AUTOCOMPLETE } from
import AlertComponent from "../alert/alert.component";
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
const { Option } = Select;
const JobSearchSelect = ({
disabled,
convertedOnly = false,
@@ -85,24 +87,24 @@ const JobSearchSelect = ({
style={{ width: "100%" }}
suffixIcon={(loading || idLoading) && <Spin />} // matches OLD spinner semantics
notFoundContent={loading ? <LoadingOutlined /> : null} // matches OLD (loading only)
options={theOptions?.map((o) => ({
key: o.id,
value: o.id,
status: o.status,
label: (
<Space align="center">
<span>
{`${clm_no && o.clm_no ? `${o.clm_no} | ` : ""}${o.ro_number || t("general.labels.na")} | ${OwnerNameDisplayFunction(
o
)} | ${o.v_model_yr || ""} ${o.v_make_desc || ""} ${o.v_model_desc || ""}`}
</span>
<Tag>
<strong>{o.status}</strong>
</Tag>
</Space>
)
}))}
/>
>
{theOptions
? theOptions.map((o) => (
<Option key={o.id} value={o.id} status={o.status}>
<Space align="center">
<span>
{`${clm_no && o.clm_no ? `${o.clm_no} | ` : ""}${o.ro_number || t("general.labels.na")} | ${OwnerNameDisplayFunction(
o
)} | ${o.v_model_yr || ""} ${o.v_make_desc || ""} ${o.v_model_desc || ""}`}
</span>
<Tag>
<strong>{o.status}</strong>
</Tag>
</Space>
</Option>
))
: null}
</Select>
{error ? <AlertComponent title={error.message} type="error" /> : null}
{idError ? <AlertComponent title={idError.message} type="error" /> : null}

View File

@@ -59,12 +59,13 @@ export function JobsAdminClass({ bodyshop, job }) {
}
]}
>
<Select
options={bodyshop.md_classes.map((s) => ({
value: s,
label: s
}))}
/>
<Select>
{bodyshop.md_classes.map((s) => (
<Select.Option key={s} value={s}>
{s}
</Select.Option>
))}
</Select>
</Form.Item>
</Form>

View File

@@ -141,11 +141,13 @@ export function JobsCloseLines({ bodyshop, job, jobRO }) {
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}}
disabled={jobRO}
options={bodyshop.md_responsibility_centers.profits.map((p) => ({
value: p.name,
label: p.name
}))}
/>
>
{bodyshop.md_responsibility_centers.profits.map((p) => (
<Select.Option key={p.name} value={p.name}>
{p.name}
</Select.Option>
))}
</Select>
</Form.Item>
</td>
<td>
@@ -169,11 +171,13 @@ export function JobsCloseLines({ bodyshop, job, jobRO }) {
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}}
disabled={jobRO}
options={bodyshop.md_responsibility_centers.profits.map((p) => ({
value: p.name,
label: p.name
}))}
/>
>
{bodyshop.md_responsibility_centers.profits.map((p) => (
<Select.Option key={p.name} value={p.name}>
{p.name}
</Select.Option>
))}
</Select>
</Form.Item>
</td>
</tr>

View File

@@ -2,7 +2,7 @@ import { useMutation } from "@apollo/client/react";
import { Button, Divider, Form, Input, Modal, Select, Space, Switch } from "antd";
import axios from "axios";
import { some } from "lodash";
import { useCallback, useMemo, useState } from "react";
import { useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -19,10 +19,10 @@ import { useSocket } from "../../contexts/SocketIO/useSocket.js";
import RREarlyROForm from "../dms-post-form/rr-early-ro-form";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
jobRO: selectJobReadOnly
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation, type }) =>
dispatch(
@@ -37,17 +37,16 @@ const mapDispatchToProps = (dispatch) => ({
export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTrail, parentFormIsFieldsTouched }) {
const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false);
const [earlyRoCreated, setEarlyRoCreated] = useState(!!job?.dms_id);
const [earlyRoCreatedThisSession, setEarlyRoCreatedThisSession] = useState(false);
const [earlyRoCreated, setEarlyRoCreated] = useState(!!job?.dms_id); // Track early RO creation state
const [earlyRoCreatedThisSession, setEarlyRoCreatedThisSession] = useState(false); // Track if created in THIS modal session
const [mutationConvertJob] = useMutation(CONVERT_JOB_TO_RO);
const { t } = useTranslation();
const [form] = Form.useForm();
const notification = useNotification();
const allFormValues = Form.useWatch([], form);
const { socket } = useSocket();
const { socket } = useSocket(); // Extract socket from context
// Get Fortellis treatment for proper DMS mode detection
const {
treatments: { Fortellis }
} = useTreatmentsWithConfig({
@@ -56,64 +55,16 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
splitKey: bodyshop?.imexshopid
});
// Check if bodyshop has Reynolds integration using the proper getDmsMode function
const dmsMode = getDmsMode(bodyshop, Fortellis.treatment);
const isReynoldsMode = dmsMode === DMS_MAP.reynolds;
const insuranceOptions = useMemo(
() =>
(bodyshop?.md_ins_cos ?? []).map((s) => ({
value: s.name,
label: s.name
})),
[bodyshop?.md_ins_cos]
);
const classOptions = useMemo(
() =>
(bodyshop?.md_classes ?? []).map((s) => ({
value: s,
label: s
})),
[bodyshop?.md_classes]
);
const referralOptions = useMemo(
() =>
(bodyshop?.md_referral_sources ?? []).map((s) => ({
value: s,
label: s
})),
[bodyshop?.md_referral_sources]
);
const csrOptions = useMemo(
() =>
(bodyshop?.employees ?? [])
.filter((emp) => emp.active)
.map((emp) => ({
value: emp.id,
label: `${emp.first_name} ${emp.last_name}`
})),
[bodyshop?.employees]
);
const categoryOptions = useMemo(
() =>
(bodyshop?.md_categories ?? []).map((s) => ({
value: s,
label: s
})),
[bodyshop?.md_categories]
);
const handleConvert = async ({ employee_csr, category, ...values }) => {
if (parentFormIsFieldsTouched()) {
alert(t("jobs.labels.savebeforeconversion"));
return;
}
setLoading(true);
const res = await mutationConvertJob({
variables: {
jobId: job.id,
@@ -127,11 +78,13 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
});
if (values.ca_gst_registrant) {
await axios.post("/job/totalsssu", { id: job.id });
await axios.post("/job/totalsssu", {
id: job.id
});
}
if (!res.errors) {
refetch?.();
refetch();
notification.success({
title: t("jobs.successes.converted")
});
@@ -144,20 +97,19 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
setOpen(false);
}
setLoading(false);
};
const submitDisabled = useCallback(() => some(allFormValues, (v) => v === undefined), [allFormValues]);
const handleEarlyROSuccess = (result) => {
setEarlyRoCreated(true);
setEarlyRoCreatedThisSession(true);
setEarlyRoCreated(true); // Mark early RO as created
setEarlyRoCreatedThisSession(true); // Mark as created in this session
notification.success({
title: t("jobs.successes.early_ro_created"),
description: `RO Number: ${result.roNumber || "N/A"}`
});
// Delay refetch to keep success message visible for 2 seconds
setTimeout(() => {
refetch?.();
}, 2000);
@@ -178,28 +130,29 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
disabled={job.converted || jobRO}
loading={loading}
onClick={() => {
setEarlyRoCreated(!!job?.dms_id);
setEarlyRoCreatedThisSession(false);
setEarlyRoCreated(!!job?.dms_id); // Initialize state based on current job
setEarlyRoCreatedThisSession(false); // Reset session state when opening modal
setOpen(true);
}}
>
{t("jobs.actions.convert")}
</Button>
{/* Convert Job Modal */}
<Modal
open={open}
onCancel={handleModalClose}
closable={!(earlyRoCreatedThisSession && !job.converted)}
maskClosable={!(earlyRoCreatedThisSession && !job.converted)}
closable={!(earlyRoCreatedThisSession && !job.converted)} // Only restrict if created in THIS session
maskClosable={!(earlyRoCreatedThisSession && !job.converted)} // Only restrict if created in THIS session
title={t("jobs.actions.convert")}
footer={null}
width={700}
destroyOnHidden
>
{/* Standard Convert Form */}
<Form
layout="vertical"
form={form}
preserve={false}
onFinish={handleConvert}
initialValues={{
driveable: true,
@@ -211,6 +164,7 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
referral_source_extra: job.referral_source_extra ?? ""
}}
>
{/* Show Reynolds Early RO section at the top if applicable */}
{isReynoldsMode && !job.dms_id && !earlyRoCreated && (
<>
<RREarlyROForm
@@ -227,78 +181,127 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
<Form.Item
name={["ins_co_nm"]}
label={t("jobs.fields.ins_co_nm")}
rules={[{ required: true }]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Select
showSearch={{
optionFilterProp:'label'
}}
options={insuranceOptions}
/>
<Select showSearch>
{bodyshop.md_ins_cos.map((s, i) => (
<Select.Option key={i} value={s.name}>
{s.name}
</Select.Option>
))}
</Select>
</Form.Item>
{bodyshop.enforce_class && (
<Form.Item name="class" label={t("jobs.fields.class")} rules={[{ required: bodyshop.enforce_class }]}>
<Select options={classOptions} />
<Form.Item
name={"class"}
label={t("jobs.fields.class")}
rules={[
{
required: bodyshop.enforce_class
//message: t("general.validation.required"),
}
]}
>
<Select>
{bodyshop.md_classes.map((s) => (
<Select.Option key={s} value={s}>
{s}
</Select.Option>
))}
</Select>
</Form.Item>
)}
{bodyshop.enforce_referral && (
<>
<Form.Item
name="referral_source"
name={"referral_source"}
label={t("jobs.fields.referralsource")}
rules={[{ required: bodyshop.enforce_referral }]}
rules={[
{
required: bodyshop.enforce_referral
//message: t("general.validation.required"),
}
]}
>
<Select options={referralOptions} />
<Select>
{bodyshop.md_referral_sources.map((s) => (
<Select.Option key={s} value={s}>
{s}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label={t("jobs.fields.referral_source_extra")} name="referral_source_extra">
<Input />
</Form.Item>
</>
)}
{bodyshop.enforce_conversion_csr && (
<Form.Item
name="employee_csr"
name={"employee_csr"}
label={t(
InstanceRenderManager({
imex: "jobs.fields.employee_csr",
rome: "jobs.fields.employee_csr_writer"
})
)}
rules={[{ required: bodyshop.enforce_conversion_csr }]}
rules={[
{
required: bodyshop.enforce_conversion_csr
//message: t("general.validation.required"),
}
]}
>
<Select
showSearch={{
optionFilterProp: 'label',
filterOption: (input, option) =>
(option?.label ?? "").toLowerCase().includes(input.toLowerCase())
optionFilterProp: "children",
filterOption: (input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}}
style={{ width: 200 }}
options={csrOptions}
/>
>
{bodyshop.employees
.filter((emp) => emp.active)
.map((emp) => (
<Select.Option value={emp.id} key={emp.id} name={`${emp.first_name} ${emp.last_name}`}>
{`${emp.first_name} ${emp.last_name}`}
</Select.Option>
))}
</Select>
</Form.Item>
)}
{bodyshop.enforce_conversion_category && (
<Form.Item name="category" label={t("jobs.fields.category")} rules={[{ required: bodyshop.enforce_conversion_category }]}>
<Select allowClear options={categoryOptions} />
<Form.Item
name={"category"}
label={t("jobs.fields.category")}
rules={[
{
required: bodyshop.enforce_conversion_category
//message: t("general.validation.required"),
}
]}
>
<Select allowClear>
{bodyshop.md_categories.map((s) => (
<Select.Option key={s} value={s}>
{s}
</Select.Option>
))}
</Select>
</Form.Item>
)}
{bodyshop.region_config.toLowerCase().startsWith("ca") && (
<Form.Item label={t("jobs.fields.ca_gst_registrant")} name="ca_gst_registrant" valuePropName="checked">
<Switch />
</Form.Item>
)}
<Form.Item label={t("jobs.fields.driveable")} name="driveable" valuePropName="checked">
<Switch />
</Form.Item>
<Form.Item label={t("jobs.fields.towin")} name="towin" valuePropName="checked">
<Switch />
</Form.Item>
@@ -313,7 +316,6 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
>
{t("jobs.actions.convert")}
</Button>
<Button onClick={handleModalClose} disabled={earlyRoCreatedThisSession && !job.converted}>
{t("general.actions.close")}
</Button>

View File

@@ -60,13 +60,13 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
<Input />
</Form.Item>
<Form.Item label={t("jobs.fields.ins_co_nm")} name="ins_co_nm">
<Select
onChange={handleInsCoChange}
options={bodyshop.md_ins_cos.map((s) => ({
value: s.name,
label: s.name
}))}
/>
<Select onChange={handleInsCoChange}>
{bodyshop.md_ins_cos.map((s) => (
<Select.Option key={s.name} value={s.name}>
{s.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label={t("jobs.fields.ins_addr1")} name="ins_addr1">
<Input />
@@ -192,12 +192,13 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
<Input />
</Form.Item>
<Form.Item label={t("jobs.fields.referralsource")} name="referral_source">
<Select
options={bodyshop.md_referral_sources.map((s) => ({
value: s,
label: s
}))}
/>
<Select>
{bodyshop.md_referral_sources.map((s) => (
<Select.Option key={s} value={s}>
{s}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label={t("jobs.fields.referral_source_extra")} name="referral_source_extra">
<Input />
@@ -220,13 +221,10 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
<CurrencyInput min={0} />
</Form.Item>
<Form.Item label={t("jobs.fields.ded_status")} name="ded_status">
<Select
allowClear
options={[
{ value: "W", label: t("jobs.labels.deductible.waived") },
{ value: "Y", label: t("jobs.labels.deductible.stands") }
]}
/>
<Select allowClear>
<Select.Option value="W">{t("jobs.labels.deductible.waived")}</Select.Option>
<Select.Option value="Y">{t("jobs.labels.deductible.stands")}</Select.Option>
</Select>
</Form.Item>
<Form.Item label={t("jobs.fields.depreciation_taxes")} name="depreciation_taxes">
<CurrencyInput />

View File

@@ -43,19 +43,20 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.ded_status")} name="ded_status">
<Select disabled={jobRO} options={[
{ value: "W", label: t("jobs.labels.deductible.waived") },
{ value: "Y", label: t("jobs.labels.deductible.stands") }
]} />
<Select disabled={jobRO}>
<Select.Option value="W">{t("jobs.labels.deductible.waived")}</Select.Option>
<Select.Option value="Y">{t("jobs.labels.deductible.stands")}</Select.Option>
</Select>
</Form.Item>
<Form.Item label={t("jobs.fields.ded_amt")} name="ded_amt">
<CurrencyInput disabled={jobRO} min={0} />
</Form.Item>
<Form.Item label={t("jobs.fields.ded_note")} name="ded_note">
<Select disabled={jobRO} options={bodyshop.md_ded_notes.map((n) => ({
value: n,
label: n
}))} />
<Select disabled={jobRO}>
{bodyshop.md_ded_notes.map((n, index) => (
<Select.Option key={index}>{n}</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label={t("jobs.fields.policy_no")} name="policy_no">
<Input disabled={jobRO} />
@@ -65,10 +66,13 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
</Form.Item>
<Form.Item label={t("jobs.fields.ins_co_nm")} name="ins_co_nm">
<Select disabled={jobRO} onChange={handleInsCoChange} options={bodyshop.md_ins_cos.map((s) => ({
value: s.name,
label: s.name
}))} />
<Select disabled={jobRO} onChange={handleInsCoChange}>
{bodyshop.md_ins_cos.map((s) => (
<Select.Option key={s.name} value={s.name}>
{s.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label={t("jobs.fields.ins_addr1")} name="ins_addr1">
<Input disabled={jobRO} />
@@ -119,19 +123,25 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
}
]}
>
<Select disabled={jobRO} allowClear options={bodyshop.md_referral_sources.map((s) => ({
value: s,
label: s
}))} />
<Select disabled={jobRO} allowClear>
{bodyshop.md_referral_sources.map((s) => (
<Select.Option key={s} value={s}>
{s}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label={t("jobs.fields.referral_source_extra")} name="referral_source_extra">
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.alt_transport")} name="alt_transport">
<Select disabled={jobRO} allowClear options={bodyshop.appt_alt_transport.map((s) => ({
value: s,
label: s
}))} />
<Select disabled={jobRO} allowClear>
{bodyshop.appt_alt_transport.map((s) => (
<Select.Option key={s} value={s}>
{s}
</Select.Option>
))}
</Select>
</Form.Item>
</FormRow>
<Row gutter={[16, 16]}>
@@ -233,10 +243,13 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
</FormRow>
<FormRow header={t("jobs.forms.other")}>
<Form.Item label={t("jobs.fields.category")} name="category">
<Select disabled={jobRO} allowClear options={bodyshop.md_categories.map((s) => ({
value: s,
label: s
}))} />
<Select disabled={jobRO} allowClear>
{bodyshop.md_categories.map((s) => (
<Select.Option key={s} value={s}>
{s}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label={t("jobs.fields.selling_dealer")} name="selling_dealer">
<Input disabled={jobRO} />

View File

@@ -714,12 +714,13 @@ export function JobsDetailHeaderActions({
<FormDateTimePickerComponent />
</Form.Item>
<Form.Item label={t("appointments.fields.color")} name="color">
<Select
options={bodyshop.appt_colors.map((col) => ({
value: col.color.hex,
label: col.label
}))}
/>
<Select>
{bodyshop.appt_colors.map((col, idx) => (
<Select.Option key={idx} value={col.color.hex}>
{col.label}
</Select.Option>
))}
</Select>
</Form.Item>
<Space wrap>

View File

@@ -94,26 +94,22 @@ export function LaborAllocationsAdjustmentEdit({
}
]}
>
<Select
allowClear
disabled={!!mod_lbr_ty}
options={[
{ value: "LAA", label: t("joblines.fields.lbr_types.LAA") },
{ value: "LAB", label: t("joblines.fields.lbr_types.LAB") },
{ value: "LAD", label: t("joblines.fields.lbr_types.LAD") },
{ value: "LAE", label: t("joblines.fields.lbr_types.LAE") },
{ value: "LAF", label: t("joblines.fields.lbr_types.LAF") },
{ value: "LAG", label: t("joblines.fields.lbr_types.LAG") },
{ value: "LAM", label: t("joblines.fields.lbr_types.LAM") },
{ value: "LAR", label: t("joblines.fields.lbr_types.LAR") },
{ value: "LAS", label: t("joblines.fields.lbr_types.LAS") },
{ value: "LAU", label: t("joblines.fields.lbr_types.LAU") },
{ value: "LA1", label: t("joblines.fields.lbr_types.LA1") },
{ value: "LA2", label: t("joblines.fields.lbr_types.LA2") },
{ value: "LA3", label: t("joblines.fields.lbr_types.LA3") },
{ value: "LA4", label: t("joblines.fields.lbr_types.LA4") }
]}
/>
<Select allowClear disabled={!!mod_lbr_ty}>
<Select.Option value="LAA">{t("joblines.fields.lbr_types.LAA")}</Select.Option>
<Select.Option value="LAB">{t("joblines.fields.lbr_types.LAB")}</Select.Option>
<Select.Option value="LAD">{t("joblines.fields.lbr_types.LAD")}</Select.Option>
<Select.Option value="LAE">{t("joblines.fields.lbr_types.LAE")}</Select.Option>
<Select.Option value="LAF">{t("joblines.fields.lbr_types.LAF")}</Select.Option>
<Select.Option value="LAG">{t("joblines.fields.lbr_types.LAG")}</Select.Option>
<Select.Option value="LAM">{t("joblines.fields.lbr_types.LAM")}</Select.Option>
<Select.Option value="LAR">{t("joblines.fields.lbr_types.LAR")}</Select.Option>
<Select.Option value="LAS">{t("joblines.fields.lbr_types.LAS")}</Select.Option>
<Select.Option value="LAU">{t("joblines.fields.lbr_types.LAU")}</Select.Option>
<Select.Option value="LA1">{t("joblines.fields.lbr_types.LA1")}</Select.Option>
<Select.Option value="LA2">{t("joblines.fields.lbr_types.LA2")}</Select.Option>
<Select.Option value="LA3">{t("joblines.fields.lbr_types.LA3")}</Select.Option>
<Select.Option value="LA4">{t("joblines.fields.lbr_types.LA4")}</Select.Option>
</Select>
</Form.Item>
<Form.Item
label={t("jobs.fields.adjustmenthours")}

View File

@@ -7,6 +7,8 @@ import { SEARCH_OWNERS_BY_ID_FOR_AUTOCOMPLETE, SEARCH_OWNERS_FOR_AUTOCOMPLETE }
import AlertComponent from "../alert/alert.component";
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
const { Option } = Select;
const OwnerSearchSelect = ({ value, onChange, onBlur, disabled, ref }) => {
const [callSearch, { loading, error, data }] = useLazyQuery(SEARCH_OWNERS_FOR_AUTOCOMPLETE);
@@ -69,12 +71,15 @@ const OwnerSearchSelect = ({ value, onChange, onBlur, disabled, ref }) => {
onSelect={handleSelect}
notFoundContent={loading ? <LoadingOutlined /> : <Empty />}
onBlur={onBlur}
options={theOptions?.map((o) => ({
key: o.id,
value: o.id,
label: `${OwnerNameDisplayFunction(o)} | ${o.ownr_addr1 || ""} `
}))}
/>
>
{theOptions
? theOptions.map((o) => (
<Option key={o.id} value={o.id}>
{`${OwnerNameDisplayFunction(o)} | ${o.ownr_addr1 || ""} `}
</Option>
))
: null}
</Select>
{idLoading || loading ? <LoadingOutlined /> : null}
{error ? <AlertComponent title={error.message} type="error" /> : null}
{idError ? <AlertComponent title={idError.message} type="error" /> : null}

View File

@@ -158,21 +158,19 @@ export function PartsOrderModalComponent({
key={`${index}part_type`}
name={[field.name, "part_type"]}
>
<Select
disabled={!(sendType === "oec" && OEConnection_PriceChange.treatment === "on")}
options={[
{ value: "PAA", label: t("joblines.fields.part_types.PAA") },
{ value: "PAC", label: t("joblines.fields.part_types.PAC") },
{ value: "PAL", label: t("joblines.fields.part_types.PAL") },
{ value: "PAG", label: t("joblines.fields.part_types.PAG") },
{ value: "PAM", label: t("joblines.fields.part_types.PAM") },
{ value: "PAP", label: t("joblines.fields.part_types.PAP") },
{ value: "PAN", label: t("joblines.fields.part_types.PAN") },
{ value: "PAO", label: t("joblines.fields.part_types.PAO") },
{ value: "PAR", label: t("joblines.fields.part_types.PAR") },
{ value: "PAS", label: t("joblines.fields.part_types.PAS") }
]}
/>
<Select disabled={!(sendType === "oec" && OEConnection_PriceChange.treatment === "on")}>
<Select.Option value="PAA">{t("joblines.fields.part_types.PAA")}</Select.Option>
<Select.Option value="PAC">{t("joblines.fields.part_types.PAC")}</Select.Option>
<Select.Option value="PAL">{t("joblines.fields.part_types.PAL")}</Select.Option>
<Select.Option value="PAG">{t("joblines.fields.part_types.PAG")}</Select.Option>
<Select.Option value="PAM">{t("joblines.fields.part_types.PAM")}</Select.Option>
<Select.Option value="PAP">{t("joblines.fields.part_types.PAP")}</Select.Option>
<Select.Option value="PAN">{t("joblines.fields.part_types.PAN")}</Select.Option>
<Select.Option value="PAO">{t("joblines.fields.part_types.PAO")}</Select.Option>
<Select.Option value="PAR">{t("joblines.fields.part_types.PAR")}</Select.Option>
<Select.Option value="PAS">{t("joblines.fields.part_types.PAS")}</Select.Option>
</Select>
</Form.Item>
<Form.Item
label={t("parts_orders.fields.oem_partno")}

View File

@@ -29,12 +29,13 @@ export function PartsReceiveModalComponent({ bodyshop, form }) {
})
});
}}
options={bodyshop.md_parts_locations.map((loc, idx) => ({
key: idx,
value: loc,
label: loc
}))}
/>
>
{bodyshop.md_parts_locations.map((loc, idx) => (
<Select.Option key={idx} value={loc}>
{loc}
</Select.Option>
))}
</Select>
</Form.Item>
</LayoutFormRow>
<Typography.Title level={4}>{t("parts_orders.labels.inthisorder")}</Typography.Title>
@@ -84,14 +85,13 @@ export function PartsReceiveModalComponent({ bodyshop, form }) {
key={`${index}location`}
name={[field.name, "location"]}
>
<Select
style={{ width: "10rem" }}
options={bodyshop.md_parts_locations.map((loc, idx) => ({
key: idx,
value: loc,
label: loc
}))}
/>
<Select style={{ width: "10rem" }}>
{bodyshop.md_parts_locations.map((loc, idx) => (
<Select.Option key={idx} value={loc}>
{loc}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("parts_orders.fields.quantity")}

View File

@@ -2,6 +2,8 @@ import { Button, Card, Divider, Form, Input, Select, Space } from "antd";
import { DeleteOutlined, PlusOutlined } from "@ant-design/icons";
import { useTranslation } from "react-i18next";
const { Option } = Select;
export default function PartsShopInfoEmailPresets() {
const { t } = useTranslation();
@@ -24,7 +26,13 @@ export default function PartsShopInfoEmailPresets() {
label={t("bodyshop.labels.email_type")}
rules={[{ required: true, message: t("bodyshop.errors.email_type_required") }]}
>
<Select placeholder={t("bodyshop.placeholders.select_email_type")} options={emailTypes} />
<Select placeholder={t("bodyshop.placeholders.select_email_type")}>
{emailTypes.map((type) => (
<Option key={type.value} value={type.value}>
{type.label}
</Option>
))}
</Select>
</Form.Item>
<Form.Item
{...restField}

View File

@@ -91,25 +91,20 @@ export function PaymentFormComponent({ form, bodyshop, disabled }) {
}
]}
>
<Select disabled={disabled}
options={Qb_Multi_Ar.treatment === "on"
? [
{ value: t("payments.labels.customer"), label: t("payments.labels.customer") },
{
label: t("payments.labels.external"),
options: bodyshop.md_ins_cos.map((i, idx) => ({
key: idx,
value: i.name,
label: i.name
}))
}
]
: [
{ value: t("payments.labels.customer"), label: t("payments.labels.customer") },
{ value: t("payments.labels.insurance"), label: t("payments.labels.insurance") }
]
}
/>
<Select disabled={disabled}>
<Select.Option value={t("payments.labels.customer")}>{t("payments.labels.customer")}</Select.Option>
{Qb_Multi_Ar.treatment === "on" ? (
<Select.OptGroup label={t("payments.labels.external")}>
{bodyshop.md_ins_cos.map((i, idx) => (
<Select.Option key={idx} value={i.name}>
{i.name}
</Select.Option>
))}
</Select.OptGroup>
) : (
<Select.Option value={t("payments.labels.insurance")}>{t("payments.labels.insurance")}</Select.Option>
)}
</Select>
</Form.Item>
<Form.Item
@@ -122,13 +117,13 @@ export function PaymentFormComponent({ form, bodyshop, disabled }) {
}
]}
>
<Select disabled={disabled}
options={bodyshop.md_payment_types.map((v, idx) => ({
key: idx,
value: v,
label: v
}))}
/>
<Select disabled={disabled}>
{bodyshop.md_payment_types.map((v, idx) => (
<Select.Option key={idx} value={v}>
{v}
</Select.Option>
))}
</Select>
</Form.Item>
</LayoutFormRow>
<LayoutFormRow grow>

View File

@@ -453,10 +453,10 @@ export function ProductionListConfigManager({
}}
onSelect={handleSelect}
placeholder={t("production.labels.selectview")}
optionLabelProp="label"
popupMatchSelectWidth={false}
value={activeView}
disabled={open || isAddingNewProfile} // Disable the Select box when the popover is open or adding a new profile
optionLabelProp="label"
>
{bodyshop?.production_config &&
bodyshop.production_config

View File

@@ -158,28 +158,20 @@ export function ScheduleJobModalComponent({
</LayoutFormRow>
<LayoutFormRow grow>
<Form.Item name="color" label={t("appointments.fields.color")}>
<Select
allowClear
options={
bodyshop.appt_colors &&
bodyshop.appt_colors.map((color) => ({
value: color.color.hex,
label: color.label
}))
}
/>
<Select allowClear>
{bodyshop.appt_colors &&
bodyshop.appt_colors.map((color) => (
<Select.Option style={{ color: color.color.hex }} key={color.color.hex} value={color.color.hex}>
{color.label}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item name={"alt_transport"} label={t("jobs.fields.alt_transport")}>
<Select
allowClear
options={
bodyshop.appt_alt_transport &&
bodyshop.appt_alt_transport.map((alt) => ({
value: alt,
label: alt
}))
}
/>
<Select allowClear>
{bodyshop.appt_alt_transport &&
bodyshop.appt_alt_transport.map((alt) => <Select.Option key={alt}>{alt}</Select.Option>)}
</Select>
</Form.Item>
<Form.Item name={"note"} label={t("appointments.fields.note")}>
<Input />

View File

@@ -120,12 +120,13 @@ export function ScheduleManualEvent({ bodyshop, event }) {
<FormDateTimePickerComponent />
</Form.Item>
<Form.Item label={t("appointments.fields.color")} name="color">
<Select
options={bodyshop.appt_colors.map((col) => ({
value: col.color.hex,
label: col.label
}))}
/>
<Select>
{bodyshop.appt_colors.map((col, idx) => (
<Select.Option key={idx} value={col.color.hex}>
{col.label}
</Select.Option>
))}
</Select>
</Form.Item>
<Space wrap>

View File

@@ -325,20 +325,22 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
}
]}
>
<Select
options={[
{ value: "timetickets.labels.shift", label: t("timetickets.labels.shift") },
...(bodyshop.cdk_dealerid ||
bodyshop.pbs_serialnumber ||
bodyshop.rr_dealerid ||
Enhanced_Payroll.treatment === "on"
? CiecaSelect(false, true)
: bodyshop.md_responsibility_centers.costs.map((c) => ({
value: c.name,
label: c.name
})))
]}
/>
<Select>
<Select.Option key={"shift"} value="timetickets.labels.shift">
{t("timetickets.labels.shift")}
</Select.Option>
{bodyshop.cdk_dealerid ||
bodyshop.pbs_serialnumber ||
bodyshop.rr_dealerid ||
Enhanced_Payroll.treatment === "on"
? CiecaSelect(false, true)
: bodyshop.md_responsibility_centers.costs.map((c) => (
<Select.Option key={c.name} value={c.name}>
{c.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("employees.fields.rate")}

View File

@@ -1039,25 +1039,22 @@ export function ShopInfoGeneral({ form }) {
key={`${index}mod_lbr_ty`}
name={[field.name, "mod_lbr_ty"]}
>
<Select
allowClear
options={[
{ value: "LAA", label: t("joblines.fields.lbr_types.LAA") },
{ value: "LAB", label: t("joblines.fields.lbr_types.LAB") },
{ value: "LAD", label: t("joblines.fields.lbr_types.LAD") },
{ value: "LAE", label: t("joblines.fields.lbr_types.LAE") },
{ value: "LAF", label: t("joblines.fields.lbr_types.LAF") },
{ value: "LAG", label: t("joblines.fields.lbr_types.LAG") },
{ value: "LAM", label: t("joblines.fields.lbr_types.LAM") },
{ value: "LAR", label: t("joblines.fields.lbr_types.LAR") },
{ value: "LAS", label: t("joblines.fields.lbr_types.LAS") },
{ value: "LAU", label: t("joblines.fields.lbr_types.LAU") },
{ value: "LA1", label: t("joblines.fields.lbr_types.LA1") },
{ value: "LA2", label: t("joblines.fields.lbr_types.LA2") },
{ value: "LA3", label: t("joblines.fields.lbr_types.LA3") },
{ value: "LA4", label: t("joblines.fields.lbr_types.LA4") }
]}
/>
<Select allowClear>
<Select.Option value="LAA">{t("joblines.fields.lbr_types.LAA")}</Select.Option>
<Select.Option value="LAB">{t("joblines.fields.lbr_types.LAB")}</Select.Option>
<Select.Option value="LAD">{t("joblines.fields.lbr_types.LAD")}</Select.Option>
<Select.Option value="LAE">{t("joblines.fields.lbr_types.LAE")}</Select.Option>
<Select.Option value="LAF">{t("joblines.fields.lbr_types.LAF")}</Select.Option>
<Select.Option value="LAG">{t("joblines.fields.lbr_types.LAG")}</Select.Option>
<Select.Option value="LAM">{t("joblines.fields.lbr_types.LAM")}</Select.Option>
<Select.Option value="LAR">{t("joblines.fields.lbr_types.LAR")}</Select.Option>
<Select.Option value="LAS">{t("joblines.fields.lbr_types.LAS")}</Select.Option>
<Select.Option value="LAU">{t("joblines.fields.lbr_types.LAU")}</Select.Option>
<Select.Option value="LA1">{t("joblines.fields.lbr_types.LA1")}</Select.Option>
<Select.Option value="LA2">{t("joblines.fields.lbr_types.LA2")}</Select.Option>
<Select.Option value="LA3">{t("joblines.fields.lbr_types.LA3")}</Select.Option>
<Select.Option value="LA4">{t("joblines.fields.lbr_types.LA4")}</Select.Option>
</Select>
</Form.Item>
<Form.Item
label={t("joblines.fields.mod_lb_hrs")}
@@ -1071,20 +1068,17 @@ export function ShopInfoGeneral({ form }) {
key={`${index}part_type`}
name={[field.name, "part_type"]}
>
<Select
allowClear
options={[
{ value: "PAA", label: t("joblines.fields.part_types.PAA") },
{ value: "PAC", label: t("joblines.fields.part_types.PAC") },
{ value: "PAE", label: t("joblines.fields.part_types.PAE") },
{ value: "PAL", label: t("joblines.fields.part_types.PAL") },
{ value: "PAM", label: t("joblines.fields.part_types.PAM") },
{ value: "PAN", label: t("joblines.fields.part_types.PAN") },
{ value: "PAO", label: t("joblines.fields.part_types.PAO") },
{ value: "PAR", label: t("joblines.fields.part_types.PAR") },
{ value: "PAS", label: t("joblines.fields.part_types.PAS") }
]}
/>
<Select allowClear>
<Select.Option value="PAA">{t("joblines.fields.part_types.PAA")}</Select.Option>
<Select.Option value="PAC">{t("joblines.fields.part_types.PAC")}</Select.Option>
<Select.Option value="PAE">{t("joblines.fields.part_types.PAE")}</Select.Option>
<Select.Option value="PAL">{t("joblines.fields.part_types.PAL")}</Select.Option>
<Select.Option value="PAM">{t("joblines.fields.part_types.PAM")}</Select.Option>
<Select.Option value="PAN">{t("joblines.fields.part_types.PAN")}</Select.Option>
<Select.Option value="PAO">{t("joblines.fields.part_types.PAO")}</Select.Option>
<Select.Option value="PAR">{t("joblines.fields.part_types.PAR")}</Select.Option>
<Select.Option value="PAS">{t("joblines.fields.part_types.PAS")}</Select.Option>
</Select>
</Form.Item>
<Form.Item
label={t("joblines.fields.oem_partno")}

View File

@@ -51,7 +51,13 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
}
]}
>
<Select options={Object.keys(ConfigFormTypes).map((i) => ({ value: i, label: i }))} />
<Select>
{Object.keys(ConfigFormTypes).map((i) => (
<Select.Option key={i} value={i}>
{i}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("jobs.fields.intake.label")}
@@ -150,13 +156,13 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
}
]}
>
<Select
mode="multiple"
options={Object.keys(TemplateListGenerated).map((i) => ({
value: TemplateListGenerated[i].key,
label: TemplateListGenerated[i].title
}))}
/>
<Select mode="multiple">
{Object.keys(TemplateListGenerated).map((i) => (
<Select.Option key={TemplateListGenerated[i].key} value={TemplateListGenerated[i].key}>
{TemplateListGenerated[i].title}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name={["intakechecklist", "next_contact_hours"]}
@@ -199,7 +205,13 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
}
]}
>
<Select options={Object.keys(ConfigFormTypes).map((i) => ({ value: i, label: i }))} />
<Select>
{Object.keys(ConfigFormTypes).map((i) => (
<Select.Option key={i} value={i}>
{i}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
@@ -298,13 +310,13 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
}
]}
>
<Select
mode="multiple"
options={Object.keys(TemplateListGenerated).map((i) => ({
value: TemplateListGenerated[i].key,
label: TemplateListGenerated[i].title
}))}
/>
<Select mode="multiple">
{Object.keys(TemplateListGenerated).map((i) => (
<Select.Option key={TemplateListGenerated[i].key} value={TemplateListGenerated[i].key}>
{TemplateListGenerated[i].title}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name={["deliverchecklist", "actual_delivery"]}

View File

@@ -80,7 +80,13 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
}
]}
>
<Select mode="multiple" options={options.map((item) => ({ value: item, label: item }))} />
<Select mode="multiple">
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name={["md_ro_statuses", "pre_production_statuses"]}
@@ -93,7 +99,13 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
}
]}
>
<Select mode="multiple" options={options.map((item) => ({ value: item, label: item }))} />
<Select mode="multiple">
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name={["md_ro_statuses", "production_statuses"]}
@@ -106,7 +118,13 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
}
]}
>
<Select mode="multiple" options={options.map((item) => ({ value: item, label: item }))} />
<Select mode="multiple">
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name={["md_ro_statuses", "post_production_statuses"]}
@@ -119,7 +137,13 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
}
]}
>
<Select mode="multiple" options={options.map((item) => ({ value: item, label: item }))} />
<Select mode="multiple">
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name={["md_ro_statuses", "ready_statuses"]}
@@ -132,7 +156,13 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
}
]}
>
<Select mode="multiple" options={options.map((item) => ({ value: item, label: item }))} />
<Select mode="multiple">
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name={["md_ro_statuses", "additional_board_statuses"]}
@@ -145,7 +175,13 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
}
]}
>
<Select mode="multiple" options={options.map((item) => ({ value: item, label: item }))} />
<Select mode="multiple">
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<LayoutFormRow noDivider>
<Form.Item
@@ -158,7 +194,13 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
]}
name={["md_ro_statuses", "default_scheduled"]}
>
<Select options={options.map((item) => ({ value: item, label: item }))} />
<Select>
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.statuses.default_arrived")}
@@ -170,7 +212,13 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
]}
name={["md_ro_statuses", "default_arrived"]}
>
<Select options={options.map((item) => ({ value: item, label: item }))} />
<Select>
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.statuses.default_exported")}
@@ -182,7 +230,13 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
]}
name={["md_ro_statuses", "default_exported"]}
>
<Select options={options.map((item) => ({ value: item, label: item }))} />
<Select>
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.statuses.default_imported")}
@@ -194,7 +248,13 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
]}
name={["md_ro_statuses", "default_imported"]}
>
<Select options={options.map((item) => ({ value: item, label: item }))} />
<Select>
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.statuses.default_invoiced")}
@@ -206,7 +266,13 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
]}
name={["md_ro_statuses", "default_invoiced"]}
>
<Select options={options.map((item) => ({ value: item, label: item }))} />
<Select>
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.statuses.default_completed")}
@@ -218,7 +284,13 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
]}
name={["md_ro_statuses", "default_completed"]}
>
<Select options={options.map((item) => ({ value: item, label: item }))} />
<Select>
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.statuses.default_delivered")}
@@ -230,7 +302,13 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
]}
name={["md_ro_statuses", "default_delivered"]}
>
<Select options={options.map((item) => ({ value: item, label: item }))} />
<Select>
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.statuses.default_void")}
@@ -242,7 +320,13 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
]}
name={["md_ro_statuses", "default_void"]}
>
<Select options={options.map((item) => ({ value: item, label: item }))} />
<Select>
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
</LayoutFormRow>
{Production_List_Status_Colors.treatment === "on" && (
@@ -268,7 +352,13 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
}
]}
>
<Select options={productionStatus.map((item) => ({ value: item, label: item }))} />
<Select>
{productionStatus.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<DeleteFilled
onClick={() => {

View File

@@ -60,13 +60,13 @@ export default function ShopInfoSpeedPrint() {
}
]}
>
<Select
mode="multiple"
options={Object.keys(TemplateListGenerated).map((key) => ({
value: TemplateListGenerated[key].key,
label: TemplateListGenerated[key].title
}))}
/>
<Select mode="multiple">
{Object.keys(TemplateListGenerated).map((key, idx) => (
<Select.Option key={idx} value={TemplateListGenerated[key].key}>
{TemplateListGenerated[key].title}
</Select.Option>
))}
</Select>
</Form.Item>
<Space wrap>

View File

@@ -43,43 +43,85 @@ export function ShopInfoIntellipay({ bodyshop, form }) {
label={t("bodyshop.fields.intellipay_config.payment_map.visa")}
name={["intellipay_config", "payment_map", "visa"]}
>
<Select showSearch options={bodyshop.md_payment_types.map((item) => ({ value: item, label: item }))} />
<Select showSearch>
{bodyshop.md_payment_types.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.intellipay_config.payment_map.mast")}
name={["intellipay_config", "payment_map", "mast"]}
>
<Select showSearch options={bodyshop.md_payment_types.map((item) => ({ value: item, label: item }))} />
<Select showSearch>
{bodyshop.md_payment_types.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.intellipay_config.payment_map.amex")}
name={["intellipay_config", "payment_map", "amex"]}
>
<Select showSearch options={bodyshop.md_payment_types.map((item) => ({ value: item, label: item }))} />
<Select showSearch>
{bodyshop.md_payment_types.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.intellipay_config.payment_map.disc")}
name={["intellipay_config", "payment_map", "disc"]}
>
<Select showSearch options={bodyshop.md_payment_types.map((item) => ({ value: item, label: item }))} />
<Select showSearch>
{bodyshop.md_payment_types.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.intellipay_config.payment_map.dnrs")}
name={["intellipay_config", "payment_map", "dnrs"]}
>
<Select showSearch options={bodyshop.md_payment_types.map((item) => ({ value: item, label: item }))} />
<Select showSearch>
{bodyshop.md_payment_types.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.intellipay_config.payment_map.jcb")}
name={["intellipay_config", "payment_map", "jcb"]}
>
<Select showSearch options={bodyshop.md_payment_types.map((item) => ({ value: item, label: item }))} />
<Select showSearch>
{bodyshop.md_payment_types.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.intellipay_config.payment_map.intr")}
name={["intellipay_config", "payment_map", "intr"]}
>
<Select showSearch options={bodyshop.md_payment_types.map((item) => ({ value: item, label: item }))} />
<Select showSearch>
{bodyshop.md_payment_types.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
</LayoutFormRow>
</>

View File

@@ -57,23 +57,21 @@ export function TechClockInComponent({ form, bodyshop, technician }) {
}
]}
>
<Select
options={
emps &&
emps.rates.map((item) => ({
value: item.cost_center,
label:
item.cost_center === "timetickets.labels.shift"
<Select>
{emps &&
emps.rates.map((item) => (
<Select.Option key={item.cost_center} value={item.cost_center}>
{item.cost_center === "timetickets.labels.shift"
? t(item.cost_center)
: bodyshop.cdk_dealerid ||
bodyshop.pbs_serialnumber ||
bodyshop.rr_dealerid ||
Enhanced_Payroll.treatment === "on"
? t(`joblines.fields.lbr_types.${item.cost_center.toUpperCase()}`)
: item.cost_center
}))
}
/>
: item.cost_center}
</Select.Option>
))}
</Select>
</Form.Item>
</LayoutFormRow>
<Divider />

View File

@@ -201,22 +201,22 @@ export function TechClockOffButton({
}
]}
>
<Select disabled={isShiftTicket}
options={
isShiftTicket
? [{ value: "timetickets.labels.shift", label: t("timetickets.labels.shift") }]
: emps &&
emps.rates.map((item) => ({
value: item.cost_center,
label:
item.cost_center === "timetickets.labels.shift"
? t(item.cost_center)
: hasDmsKey
? t(`joblines.fields.lbr_types.${item.cost_center.toUpperCase()}`)
: item.cost_center
}))
}
/>
<Select disabled={isShiftTicket}>
{isShiftTicket ? (
<Select.Option value="timetickets.labels.shift">{t("timetickets.labels.shift")}</Select.Option>
) : (
emps &&
emps.rates.map((item) => (
<Select.Option key={item.cost_center}>
{item.cost_center === "timetickets.labels.shift"
? t(item.cost_center)
: hasDmsKey
? t(`joblines.fields.lbr_types.${item.cost_center.toUpperCase()}`)
: item.cost_center}
</Select.Option>
))
)}
</Select>
</Form.Item>
{isShiftTicket ? (
@@ -232,12 +232,11 @@ export function TechClockOffButton({
}
]}
>
<Select
options={bodyshop.md_ro_statuses.production_statuses.map((item) => ({
value: item,
label: item
}))}
/>
<Select>
{bodyshop.md_ro_statuses.production_statuses.map((item) => (
<Select.Option key={item}></Select.Option>
))}
</Select>
</Form.Item>
)}

View File

@@ -91,22 +91,21 @@ export function TimeTicketModalComponent({
value={value === "timetickets.labels.shift" ? t(value) : value}
{...props}
disabled={value === "timetickets.labels.shift" || disabled}
options={
emps &&
emps.rates.map((item) => ({
value: item.cost_center,
label:
item.cost_center === "timetickets.labels.shift"
>
{emps &&
emps.rates.map((item) => (
<Select.Option key={item.cost_center} value={item.cost_center}>
{item.cost_center === "timetickets.labels.shift"
? t(item.cost_center)
: bodyshop.cdk_dealerid ||
bodyshop.pbs_serialnumber ||
bodyshop.rr_dealerid ||
Enhanced_Payroll.treatment === "on"
? t(`joblines.fields.lbr_types.${item.cost_center.toUpperCase()}`)
: item.cost_center
}))
}
/>
: item.cost_center}
</Select.Option>
))}
</Select>
);
const MemoInput = ({ value, ...props }) => (

View File

@@ -20,15 +20,13 @@ export function TimeTicketShiftFormComponent() {
}
]}
>
<Select
options={[
{ value: "timetickets.labels.amshift", label: t("timetickets.labels.amshift") },
{ value: "timetickets.labels.ambreak", label: t("timetickets.labels.ambreak") },
{ value: "timetickets.labels.lunch", label: t("timetickets.labels.lunch") },
{ value: "timetickets.labels.pmshift", label: t("timetickets.labels.pmshift") },
{ value: "timetickets.labels.pmbreak", label: t("timetickets.labels.pmbreak") }
]}
/>
<Select>
<Select.Option value="timetickets.labels.amshift">{t("timetickets.labels.amshift")}</Select.Option>
<Select.Option value="timetickets.labels.ambreak">{t("timetickets.labels.ambreak")}</Select.Option>
<Select.Option value="timetickets.labels.lunch">{t("timetickets.labels.lunch")}</Select.Option>
<Select.Option value="timetickets.labels.pmshift">{t("timetickets.labels.pmshift")}</Select.Option>
<Select.Option value="timetickets.labels.pmbreak">{t("timetickets.labels.pmbreak")}</Select.Option>
</Select>
</Form.Item>
</div>
);

View File

@@ -9,6 +9,8 @@ import {
} from "../../graphql/vehicles.queries";
import AlertComponent from "../alert/alert.component";
const { Option } = Select;
const VehicleSearchSelect = ({ value, onChange, onBlur, disabled, ref }) => {
const [callSearch, { loading, error, data }] = useLazyQuery(SEARCH_VEHICLES_FOR_AUTOCOMPLETE);
@@ -71,12 +73,15 @@ const VehicleSearchSelect = ({ value, onChange, onBlur, disabled, ref }) => {
onSelect={handleSelect}
notFoundContent={loading ? <LoadingOutlined /> : <Empty />}
onBlur={onBlur}
options={theOptions?.map((o) => ({
key: o.id,
value: o.id,
label: `${o.v_vin || ""} ${o.v_model_yr || ""} ${o.v_make_desc || ""} ${o.v_model_desc || ""} `
}))}
/>
>
{theOptions
? theOptions.map((o) => (
<Option key={o.id} value={o.id}>
{`${o.v_vin || ""} ${o.v_model_yr || ""} ${o.v_make_desc || ""} ${o.v_model_desc || ""} `}
</Option>
))
: null}
</Select>
{idLoading || loading ? <LoadingOutlined /> : null}
{error ? <AlertComponent title={error.message} type="error" /> : null}
{idError ? <AlertComponent title={idError.message} type="error" /> : null}

View File

@@ -3,6 +3,8 @@ import { Select, Space, Tag } from "antd";
import { useEffect, useState } from "react";
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
const { Option } = Select;
// To be used as a form element only.
const VendorSearchSelect = ({ value, onChange, options, onSelect, disabled, preferredMake, showPhone, ref }) => {
@@ -19,57 +21,10 @@ const VendorSearchSelect = ({ value, onChange, options, onSelect, disabled, pref
? options.filter((o) => o.favorite.filter((f) => f.toLowerCase() === preferredMake.toLowerCase()).length > 0)
: [];
const formatOption = (o, isFavorite = false) => ({
key: isFavorite ? `favorite-${o.id}` : o.id,
value: o.id,
name: o.name,
discount: o.discount,
label: (
<div
style={{
display: "flex",
alignItems: "center",
flexWrap: "nowrap",
width: "100%"
}}
>
<div
style={{
flex: 1,
minWidth: 0,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap"
}}
>
{o.name}
</div>
<Space style={{ marginLeft: "1rem" }}>
{isFavorite && <HeartOutlined style={{ color: "red" }} />}
{!isFavorite &&
o.tags?.map((tag, idx) => (
<Tag key={idx} style={{ marginLeft: "0.5rem" }}>
{tag}
</Tag>
))}
{o.phone && showPhone && <PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>}
{o.discount && o.discount !== 0 ? <Tag color="green">{`${o.discount * 100}%`}</Tag> : null}
</Space>
</div>
)
});
const allOptions = [
...(favorites?.map((o) => formatOption(o, true)) || []),
...(options?.map((o) => formatOption(o, false)) || [])
];
return (
<Select
ref={ref}
showSearch={{
optionFilterProp: "name"
}}
showSearch
value={option}
style={{
width: "100%"
@@ -104,11 +59,76 @@ const VendorSearchSelect = ({ value, onChange, options, onSelect, disabled, pref
}}
popupMatchSelectWidth={false}
onChange={setOption}
optionFilterProp="name"
onSelect={onSelect}
disabled={disabled || false}
optionLabelProp="name"
options={allOptions}
/>
>
{favorites &&
favorites.map((o) => (
<Option key={`favorite-${o.id}`} value={o.id} name={o.name} discount={o.discount}>
<div
style={{
display: "flex",
alignItems: "center",
flexWrap: "nowrap",
width: "100%"
}}
>
<div
style={{
flex: 1,
minWidth: 0,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap"
}}
>
{o.name}
</div>
<Space style={{ marginLeft: "1rem" }}>
<HeartOutlined style={{ color: "red" }} />
{o.phone && showPhone && <PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>}
{o.discount && o.discount !== 0 ? <Tag color="green">{`${o.discount * 100}%`}</Tag> : null}
</Space>
</div>
</Option>
))}
{options &&
options.map((o) => (
<Option key={o.id} value={o.id} name={o.name} discount={o.discount}>
<div
style={{
display: "flex",
alignItems: "center",
flexWrap: "nowrap",
width: "100%"
}}
>
<div
style={{
flex: 1,
minWidth: 0,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap"
}}
>
{o.name}
</div>
<Space style={{ marginLeft: "1rem" }}>
{o.tags?.map((tag, idx) => (
<Tag key={idx} style={{ marginLeft: "0.5rem" }}>
{tag}
</Tag>
))}
{o.phone && showPhone && <PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>}
{o.discount && o.discount !== 0 ? <Tag color="green">{`${o.discount * 100}%`}</Tag> : null}
</Space>
</div>
</Option>
))}
</Select>
);
};
export default VendorSearchSelect;

View File

@@ -119,13 +119,12 @@ export function DmsContainer({ setBreadcrumbs, setSelectedHeader }) {
setLogLevel(value);
socket.emit("set-log-level", value);
}}
options={[
{ key: "DEBUG", value: "DEBUG", label: "DEBUG" },
{ key: "INFO", value: "INFO", label: "INFO" },
{ key: "WARN", value: "WARN", label: "WARN" },
{ key: "ERROR", value: "ERROR", label: "ERROR" }
]}
/>
>
<Select.Option key="DEBUG">DEBUG</Select.Option>
<Select.Option key="INFO">INFO</Select.Option>
<Select.Option key="WARN">WARN</Select.Option>
<Select.Option key="ERROR">ERROR</Select.Option>
</Select>
<Button onClick={() => setLogs([])}>Clear Logs</Button>
<Button
onClick={() => {

View File

@@ -541,14 +541,13 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
setLogLevel(value);
setActiveLogLevel(value);
}}
options={[
{ key: "SILLY", value: "SILLY", label: "SILLY" },
{ key: "DEBUG", value: "DEBUG", label: "DEBUG" },
{ key: "INFO", value: "INFO", label: "INFO" },
{ key: "WARN", value: "WARN", label: "WARN" },
{ key: "ERROR", value: "ERROR", label: "ERROR" }
]}
/>
>
<Select.Option key="SILLY">SILLY</Select.Option>
<Select.Option key="DEBUG">DEBUG</Select.Option>
<Select.Option key="INFO">INFO</Select.Option>
<Select.Option key="WARN">WARN</Select.Option>
<Select.Option key="ERROR">ERROR</Select.Option>
</Select>
<Button onClick={() => setLogs([])}>Clear Logs</Button>
<Button
onClick={() => {

View File

@@ -440,15 +440,13 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, set
}
]}
>
<Select
style={{ minWidth: "12rem" }}
disabled={jobRO}
options={bodyshop.md_ins_cos.map((s) => ({
key: s.name,
value: s.name,
label: s.name
}))}
/>
<Select style={{ minWidth: "12rem" }} disabled={jobRO}>
{bodyshop.md_ins_cos.map((s) => (
<Select.Option key={s.name} value={s.name}>
{s.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item

View File

@@ -1,43 +1,44 @@
import { Select } from "antd";
import i18n from "../translations/i18n";
export default function CiecaSelect(parts = true, labor = true) {
const options = [];
if (labor) {
options.push(
{ value: "LAA", label: i18n.t("joblines.fields.lbr_types.LAA") },
{ value: "LAB", label: i18n.t("joblines.fields.lbr_types.LAB") },
{ value: "LAD", label: i18n.t("joblines.fields.lbr_types.LAD") },
{ value: "LAE", label: i18n.t("joblines.fields.lbr_types.LAE") },
{ value: "LAF", label: i18n.t("joblines.fields.lbr_types.LAF") },
{ value: "LAG", label: i18n.t("joblines.fields.lbr_types.LAG") },
{ value: "LAM", label: i18n.t("joblines.fields.lbr_types.LAM") },
{ value: "LAR", label: i18n.t("joblines.fields.lbr_types.LAR") },
{ value: "LAS", label: i18n.t("joblines.fields.lbr_types.LAS") },
{ value: "LAU", label: i18n.t("joblines.fields.lbr_types.LAU") },
{ value: "LA1", label: i18n.t("joblines.fields.lbr_types.LA1") },
{ value: "LA2", label: i18n.t("joblines.fields.lbr_types.LA2") },
{ value: "LA3", label: i18n.t("joblines.fields.lbr_types.LA3") },
{ value: "LA4", label: i18n.t("joblines.fields.lbr_types.LA4") }
);
}
if (parts) {
options.push(
{ value: "PAA", label: i18n.t("joblines.fields.part_types.PAA") },
{ value: "PAC", label: i18n.t("joblines.fields.part_types.PAC") },
{ value: "PAL", label: i18n.t("joblines.fields.part_types.PAL") },
{ value: "PAG", label: i18n.t("joblines.fields.part_types.PAG") },
{ value: "PAM", label: i18n.t("joblines.fields.part_types.PAM") },
{ value: "PAP", label: i18n.t("joblines.fields.part_types.PAP") },
{ value: "PAN", label: i18n.t("joblines.fields.part_types.PAN") },
{ value: "PAO", label: i18n.t("joblines.fields.part_types.PAO") },
{ value: "PAR", label: i18n.t("joblines.fields.part_types.PAR") },
{ value: "PAS", label: i18n.t("joblines.fields.part_types.PAS") }
);
}
return options;
return (
<>
{labor && (
<>
<Select.Option value="LAA">{i18n.t("joblines.fields.lbr_types.LAA")}</Select.Option>
<Select.Option value="LAB">{i18n.t("joblines.fields.lbr_types.LAB")}</Select.Option>
<Select.Option value="LAD">{i18n.t("joblines.fields.lbr_types.LAD")}</Select.Option>
<Select.Option value="LAE">{i18n.t("joblines.fields.lbr_types.LAE")}</Select.Option>
<Select.Option value="LAF">{i18n.t("joblines.fields.lbr_types.LAF")}</Select.Option>
<Select.Option value="LAG">{i18n.t("joblines.fields.lbr_types.LAG")}</Select.Option>
<Select.Option value="LAM">{i18n.t("joblines.fields.lbr_types.LAM")}</Select.Option>
<Select.Option value="LAR">{i18n.t("joblines.fields.lbr_types.LAR")}</Select.Option>
<Select.Option value="LAS">{i18n.t("joblines.fields.lbr_types.LAS")}</Select.Option>
<Select.Option value="LAU">{i18n.t("joblines.fields.lbr_types.LAU")}</Select.Option>
<Select.Option value="LA1">{i18n.t("joblines.fields.lbr_types.LA1")}</Select.Option>
<Select.Option value="LA2">{i18n.t("joblines.fields.lbr_types.LA2")}</Select.Option>
<Select.Option value="LA3">{i18n.t("joblines.fields.lbr_types.LA3")}</Select.Option>
<Select.Option value="LA4">{i18n.t("joblines.fields.lbr_types.LA4")}</Select.Option>
</>
)}
{parts && (
<>
<Select.Option value="PAA">{i18n.t("joblines.fields.part_types.PAA")}</Select.Option>
<Select.Option value="PAC">{i18n.t("joblines.fields.part_types.PAC")}</Select.Option>
<Select.Option value="PAL">{i18n.t("joblines.fields.part_types.PAL")}</Select.Option>
<Select.Option value="PAG">{i18n.t("joblines.fields.part_types.PAG")}</Select.Option>
<Select.Option value="PAM">{i18n.t("joblines.fields.part_types.PAM")}</Select.Option>
<Select.Option value="PAP">{i18n.t("joblines.fields.part_types.PAP")}</Select.Option>
<Select.Option value="PAN">{i18n.t("joblines.fields.part_types.PAN")}</Select.Option>
<Select.Option value="PAO">{i18n.t("joblines.fields.part_types.PAO")}</Select.Option>
<Select.Option value="PAR">{i18n.t("joblines.fields.part_types.PAR")}</Select.Option>
<Select.Option value="PAS">{i18n.t("joblines.fields.part_types.PAS")}</Select.Option>
</>
)}
</>
);
}
export function GetPartTypeName(part_type) {

1062
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,23 +18,23 @@
"job-totals-fixtures:local": "docker exec node-app /usr/bin/node /app/download-job-totals-fixtures.js"
},
"dependencies": {
"@aws-sdk/client-cloudwatch-logs": "^3.992.0",
"@aws-sdk/client-elasticache": "^3.992.0",
"@aws-sdk/client-s3": "^3.992.0",
"@aws-sdk/client-secrets-manager": "^3.992.0",
"@aws-sdk/client-ses": "^3.992.0",
"@aws-sdk/credential-provider-node": "^3.972.9",
"@aws-sdk/lib-storage": "^3.992.0",
"@aws-sdk/s3-request-presigner": "^3.992.0",
"@aws-sdk/client-cloudwatch-logs": "^3.978.0",
"@aws-sdk/client-elasticache": "^3.978.0",
"@aws-sdk/client-s3": "^3.978.0",
"@aws-sdk/client-secrets-manager": "^3.978.0",
"@aws-sdk/client-ses": "^3.978.0",
"@aws-sdk/credential-provider-node": "^3.972.3",
"@aws-sdk/lib-storage": "^3.978.0",
"@aws-sdk/s3-request-presigner": "^3.978.0",
"@opensearch-project/opensearch": "^2.13.0",
"@socket.io/admin-ui": "^0.5.1",
"@socket.io/redis-adapter": "^8.3.0",
"archiver": "^7.0.1",
"aws4": "^1.13.2",
"axios": "^1.13.5",
"axios": "^1.13.4",
"axios-curlirize": "^2.0.0",
"better-queue": "^3.8.12",
"bullmq": "^5.69.3",
"bullmq": "^5.67.2",
"chart.js": "^4.5.1",
"cloudinary": "^2.9.0",
"compression": "^1.8.1",
@@ -42,17 +42,17 @@
"cors": "^2.8.6",
"crisp-status-reporter": "^1.2.2",
"dinero.js": "^1.9.1",
"dotenv": "^17.3.1",
"dotenv": "^17.2.3",
"express": "^4.21.1",
"fast-xml-parser": "^5.3.6",
"firebase-admin": "^13.6.1",
"fast-xml-parser": "^5.3.4",
"firebase-admin": "^13.6.0",
"graphql": "^16.12.0",
"graphql-request": "^6.1.0",
"intuit-oauth": "^4.2.2",
"ioredis": "^5.9.3",
"ioredis": "^5.9.2",
"json-2-csv": "^5.5.10",
"jsonwebtoken": "^9.0.3",
"juice": "^11.1.1",
"juice": "^11.1.0",
"lodash": "^4.17.23",
"moment": "^2.30.1",
"moment-timezone": "^0.6.0",
@@ -63,13 +63,13 @@
"phone": "^3.1.70",
"query-string": "7.1.3",
"recursive-diff": "^1.0.9",
"rimraf": "^6.1.3",
"rimraf": "^6.1.2",
"skia-canvas": "^3.0.8",
"soap": "^1.7.1",
"soap": "^1.6.4",
"socket.io": "^4.8.3",
"socket.io-adapter": "^2.5.6",
"ssh2-sftp-client": "^11.0.0",
"twilio": "^5.12.2",
"twilio": "^5.12.0",
"uuid": "^11.1.0",
"winston": "^3.19.0",
"winston-cloudwatch": "^6.3.0",
@@ -82,7 +82,7 @@
"@eslint/js": "^9.39.2",
"eslint": "^9.39.2",
"eslint-plugin-react": "^7.37.5",
"globals": "^17.3.0",
"globals": "^17.2.0",
"mock-require": "^3.0.3",
"p-limit": "^3.1.0",
"prettier": "^3.8.1",

View File

@@ -264,29 +264,30 @@ const CreateRepairOrderTag = (job, errorCallback) => {
}${job.est_ct_fn ? job.est_ct_fn : ""}`
},
Dates: {
DateEstimated: (job.date_estimated && moment(job.date_estimated).format(DateFormat)) || "",
DateOpened: (job.date_opened && moment(job.date_opened).format(DateFormat)) || "",
DateScheduled:
(job.scheduled_in && moment(job.scheduled_in).tz(job.bodyshop.timezone).format(DateFormat)) || "",
DateArrived: (job.actual_in && moment(job.actual_in).tz(job.bodyshop.timezone).format(DateFormat)) || "",
DateEstimated: job.date_estimated ? moment(job.date_estimated).format(DateFormat) : "",
DateOpened: job.date_open ? moment(job.date_open).tz(job.bodyshop.timezone).format(DateFormat) : "",
DateScheduled: job.scheduled_in ? moment(job.scheduled_in).tz(job.bodyshop.timezone).format(DateFormat) : "",
DateArrived: job.actual_in ? moment(job.actual_in).tz(job.bodyshop.timezone).format(DateFormat) : "",
DateStart: job.date_repairstarted
? (job.date_repairstarted && moment(job.date_repairstarted).tz(job.bodyshop.timezone).format(DateFormat)) ||
""
: (job.actual_in && moment(job.actual_in).tz(job.bodyshop.timezone).format(DateFormat)) || "",
DateScheduledCompletion:
(job.scheduled_completion && moment(job.scheduled_completion).tz(job.bodyshop.timezone).format(DateFormat)) ||
"",
DateCompleted:
(job.actual_completion && moment(job.actual_completion).tz(job.bodyshop.timezone).format(DateFormat)) || "",
DateScheduledDelivery:
(job.scheduled_delivery && moment(job.scheduled_delivery).tz(job.bodyshop.timezone).format(DateFormat)) || "",
DateDelivered:
(job.actual_delivery && moment(job.actual_delivery).tz(job.bodyshop.timezone).format(DateFormat)) || "",
DateInvoiced:
(job.date_invoiced && moment(job.date_invoiced).tz(job.bodyshop.timezone).format(DateFormat)) || "",
DateExported:
(job.date_exported && moment(job.date_exported).tz(job.bodyshop.timezone).format(DateFormat)) || "",
DateVoid: (job.date_void && moment(job.date_void).tz(job.bodyshop.timezone).format(DateFormat)) || ""
? moment(job.date_repairstarted).tz(job.bodyshop.timezone).format(DateFormat)
: job.actual_in
? moment(job.actual_in).tz(job.bodyshop.timezone).format(DateFormat)
: "",
DateScheduledCompletion: job.scheduled_completion
? moment(job.scheduled_completion).tz(job.bodyshop.timezone).format(DateFormat)
: "",
DateCompleted: job.actual_completion
? moment(job.actual_completion).tz(job.bodyshop.timezone).format(DateFormat)
: "",
DateScheduledDelivery: job.scheduled_delivery
? moment(job.scheduled_delivery).tz(job.bodyshop.timezone).format(DateFormat)
: "",
DateDelivered: job.actual_delivery
? moment(job.actual_delivery).tz(job.bodyshop.timezone).format(DateFormat)
: "",
DateInvoiced: job.date_invoiced ? moment(job.date_invoiced).tz(job.bodyshop.timezone).format(DateFormat) : "",
DateExported: job.date_exported ? moment(job.date_exported).tz(job.bodyshop.timezone).format(DateFormat) : "",
DateVoid: job.date_void ? moment(job.date_void).tz(job.bodyshop.timezone).format(DateFormat) : ""
},
JobLineDetails: (function () {
const joblineSource = Array.isArray(job.joblines) ? job.joblines : job.joblines ? [job.joblines] : [];

View File

@@ -235,18 +235,6 @@ async function MakeFortellisCall({
// jobid: socket?.recordid
// });
if (result.data.checkStatusAfterSeconds) {
return DelayedCallback({
delayMeta: result.data,
access_token,
SubscriptionID: SubscriptionMeta.subscriptionId,
ReqId,
departmentIds: DepartmentId
});
}
logger.log(
"fortellis-log-event-json",
"DEBUG",
@@ -261,6 +249,18 @@ async function MakeFortellisCall({
},
);
if (result.data.checkStatusAfterSeconds) {
return DelayedCallback({
delayMeta: result.data,
access_token,
SubscriptionID: SubscriptionMeta.subscriptionId,
ReqId,
departmentIds: DepartmentId,
jobid,
socket
});
}
return result.data;
} catch (error) {
const errorDetails = {
@@ -310,7 +310,7 @@ async function MakeFortellisCall({
//Some Fortellis calls return a batch result that isn't ready immediately.
//This function will check the status of the call and wait until it is ready.
//It will try 5 times before giving up.
async function DelayedCallback({ delayMeta, access_token, SubscriptionID, ReqId, departmentIds }) {
async function DelayedCallback({ delayMeta, access_token, SubscriptionID, ReqId, departmentIds, jobid, socket }) {
for (let index = 0; index < 5; index++) {
await sleep(delayMeta.checkStatusAfterSeconds * 1000);
//Check to see if the call is ready.
@@ -334,6 +334,19 @@ async function DelayedCallback({ delayMeta, access_token, SubscriptionID, ReqId,
//"Department-Id": departmentIds[0].id
}
});
logger.log(
"fortellis-log-event-json-DelayedCallback",
"DEBUG",
socket?.user?.email,
jobid,
{
requestcurl: batchResult.config.curlCommand,
reqid: batchResult.config.headers["Request-Id"] || null,
subscriptionId: batchResult.config.headers["Subscription-Id"] || null,
resultdata: batchResult.data,
resultStatus: batchResult.status
},
);
// await writeFortellisLogToFile({
// timestamp: new Date().toISOString(),
// reqId: ReqId,

View File

@@ -198,7 +198,7 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
);
const existingCustomerInDMSCustList = DMSCustList.find((c) => c.customerId === selectedCustomerId);
DMSCust = existingCustomerInDMSCustList || {
customerId: selectedCustomerId //This is the fall back in case it is the generic customer.
customerId: selectedCustomerId //This is the fall back in case it is the generic customer.
};
await setSessionTransactionData(
socket.id,
@@ -207,8 +207,6 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
DMSCust,
defaultFortellisTTL
);
} else {
CreateFortellisLogEvent(socket, "DEBUG", `{3.2} Creating new customer.`);
const DMSCustomerInsertResponse = await InsertDmsCustomer({ socket, redisHelpers, JobData });
@@ -227,14 +225,10 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
CreateFortellisLogEvent(socket, "DEBUG", `{4.1} Inserting new vehicle with ID: ID ${DMSVid.vehiclesVehId}`);
DMSVeh = await InsertDmsVehicle({ socket, redisHelpers, JobData, txEnvelope, DMSVid, DMSCust });
} else {
DMSVeh = await getSessionTransactionData(
socket.id,
getTransactionType(jobid),
FortellisCacheEnums.DMSVeh
)
DMSVeh = await getSessionTransactionData(socket.id, getTransactionType(jobid), FortellisCacheEnums.DMSVeh);
CreateFortellisLogEvent(socket, "DEBUG", `{4.3} Updating Existing Vehicle to associate to owner.`);
//Check to see if the vehicle needs to be updated - i.e. the owner is not the selected customer.
//Check to see if the vehicle needs to be updated - i.e. the owner is not the selected customer.
if (!DMSVeh?.owners.find((o) => o.id.value === DMSCust.customerId && o.id.assigningPartyId === "CURRENT")) {
DMSVeh = await UpdateDmsVehicle({
socket,
@@ -271,7 +265,11 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
if (DMSTransHeader.rtnCode === "0") {
try {
CreateFortellisLogEvent(socket, "DEBUG", `{6} Attempting to post Transaction with ID ${DMSTransHeader.transID}`);
CreateFortellisLogEvent(
socket,
"DEBUG",
`{6} Attempting to post Transaction with ID ${DMSTransHeader.transID}`
);
const DmsBatchTxnPost = await PostDmsBatchWip({ socket, redisHelpers, JobData }); // 2 in 1 call that includes a post and the transactions.
await setSessionTransactionData(
@@ -282,16 +280,14 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
defaultFortellisTTL
);
if (DmsBatchTxnPost.rtnCode === "0") {
//TODO: Validate this is a string and not #
//something
CreateFortellisLogEvent(socket, "DEBUG", `{6} Successfully posted transaction to DMS.`);
await MarkJobExported({ socket, jobid: JobData.id, JobData });
await MarkJobExported({ socket, jobid: JobData.id, JobData, redisHelpers });
try {
CreateFortellisLogEvent(socket, "DEBUG", `{7} Updating Service Vehicle History.`);
const DMSVehHistory = await InsertServiceVehicleHistory({ socket, redisHelpers, JobData });
await setSessionTransactionData(
@@ -302,24 +298,19 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
defaultFortellisTTL
);
} catch (error) {
CreateFortellisLogEvent(socket, "ERROR", `{7.1} Error posting vehicle service history. ${error.message}`);
}
socket.emit("export-success", JobData.id);
} else {
//There was something wrong. Throw an error to trigger clean up.
//There was something wrong. Throw an error to trigger clean up.
//throw new Error("Error posting DMS Batch Transaction");
}
} catch (error) {
//Clean up the transaction and insert a faild error code
// //Get the error code
CreateFortellisLogEvent(socket, "DEBUG", `{6.1} Getting errors for Transaction ID ${DMSTransHeader.transID}`);
const DmsError = await QueryDmsErrWip({ socket, redisHelpers, JobData });
// //Delete the transaction
CreateFortellisLogEvent(socket, "DEBUG", `{6.2} Deleting Transaction ID ${DMSTransHeader.transID}`);
@@ -345,7 +336,11 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
stack: error.stack,
data: error.errorData
});
await InsertFailedExportLog({ socket, JobData, error: error.errorData?.issues || [JSON.stringify(error.errorData)] });
await InsertFailedExportLog({
socket,
JobData,
error: error.errorData?.issues || [JSON.stringify(error.errorData)]
});
} finally {
//Ensure we always insert logEvents
//GQL to insert logevents.
@@ -374,7 +369,7 @@ async function CalculateDmsVid({ socket, JobData, redisHelpers }) {
jobid: JobData.id,
body: {}
});
return result;
return Array.isArray(result) ? result.filter((v) => v.vehiclesVehId !== null && v.vehiclesVehId !== "") : [];
} catch (error) {
handleFortellisApiError(socket, error, "CalculateDmsVid", {
vin: JobData.v_vin,
@@ -429,12 +424,12 @@ async function QueryDmsCustomerById({ socket, redisHelpers, JobData, CustomerId
async function QueryDmsCustomerByName({ socket, redisHelpers, JobData }) {
const ownerName =
JobData.ownr_co_nm && JobData.ownr_co_nm.trim() !== ""
//? [["firstName", JobData.ownr_co_nm.replace(replaceSpecialRegex, "").toUpperCase()]] // Commented out until we receive direction.
? [["phone", JobData.ownr_ph1?.replace(replaceSpecialRegex, "")]]
? //? [["firstName", JobData.ownr_co_nm.replace(replaceSpecialRegex, "").toUpperCase()]] // Commented out until we receive direction.
[["phone", JobData.ownr_ph1?.replace(/[^0-9]/g, "")]]
: [
["firstName", JobData.ownr_fn?.replace(/[^a-zA-Z-]/g, "").toUpperCase()],
["lastName", JobData.ownr_ln?.replace(/[^a-zA-Z-]/g, "").toUpperCase()]
];
["firstName", JobData.ownr_fn?.replace(/[^a-zA-Z0-9]/g, "").toUpperCase()],
["lastName", JobData.ownr_ln?.replace(/[^a-zA-Z0-9]/g, "").toUpperCase()]
];
try {
const result = await MakeFortellisCall({
...FortellisActions.QueryCustomerByName,
@@ -457,7 +452,7 @@ async function QueryDmsCustomerByName({ socket, redisHelpers, JobData }) {
async function InsertDmsCustomer({ socket, redisHelpers, JobData }) {
try {
const isBusiness = (JobData.ownr_co_nm && JobData.ownr_co_nm.replace(replaceSpecialRegex, "").trim() !== "")
const isBusiness = JobData.ownr_co_nm && JobData.ownr_co_nm.replace(replaceSpecialRegex, "").trim() !== "";
const result = await MakeFortellisCall({
...FortellisActions.CreateCustomer,
headers: {},
@@ -466,21 +461,23 @@ async function InsertDmsCustomer({ socket, redisHelpers, JobData }) {
jobid: JobData.id,
body: {
customerType: isBusiness ? "BUSINESS" : "INDIVIDUAL",
...isBusiness ? {
companyName: JobData.ownr_co_nm && JobData.ownr_co_nm.replace(replaceSpecialRegex, "").toUpperCase(),
secondaryCustomerName: {
//lastName: JobData.ownr_co_nm && JobData.ownr_co_nm.replace(replaceSpecialRegex, "").toUpperCase()
}
} : {
customerName: {
//"suffix": "Mr.",
firstName: JobData.ownr_fn && JobData.ownr_fn.replace(/[^a-zA-Z-]/g, "").toUpperCase(),
//"middleName": "",
lastName: JobData.ownr_ln && JobData.ownr_ln.replace(/[^a-zA-Z-]/g, "").toUpperCase()
//"title": "",
//"nickName": ""
}
},
...(isBusiness
? {
companyName: JobData.ownr_co_nm && JobData.ownr_co_nm.replace(replaceSpecialRegex, "").toUpperCase(),
secondaryCustomerName: {
//lastName: JobData.ownr_co_nm && JobData.ownr_co_nm.replace(replaceSpecialRegex, "").toUpperCase()
}
}
: {
customerName: {
//"suffix": "Mr.",
firstName: JobData.ownr_fn && JobData.ownr_fn.replace(/[^a-zA-Z0-9]/g, "").toUpperCase(),
//"middleName": "",
lastName: JobData.ownr_ln && JobData.ownr_ln.replace(/[^a-zA-Z0-9]/g, "").toUpperCase()
//"title": "",
//"nickName": ""
}
}),
postalAddress: {
addressLine1: JobData.ownr_addr1?.replace(replaceSpecialRegex, "").trim().toUpperCase(),
addressLine2: JobData.ownr_addr2?.replace(replaceSpecialRegex, "").trim().toUpperCase(),
@@ -490,7 +487,7 @@ async function InsertDmsCustomer({ socket, redisHelpers, JobData }) {
rome: JobData.ownr_zip
}),
state: JobData.ownr_st?.replace(replaceSpecialRegex, "").trim().toUpperCase(),
country: JobData.ownr_ctry?.replace(replaceSpecialRegex, "").trim().toUpperCase(),
country: JobData.ownr_ctry?.replace(replaceSpecialRegex, "").trim().toUpperCase()
//"territory": ""
},
// "birthDate": {
@@ -504,7 +501,7 @@ async function InsertDmsCustomer({ socket, redisHelpers, JobData }) {
phones: [
{
//"uuid": "",
number: JobData.ownr_ph1?.replace(replaceSpecialRegex, ""),
number: JobData.ownr_ph1?.replace(/[^0-9]/g, ""),
type: "HOME"
// "doNotCallIndicator": true,
// "doNotCallIndicatorDate": `null,
@@ -540,18 +537,18 @@ async function InsertDmsCustomer({ socket, redisHelpers, JobData }) {
emailAddresses: [
...(!_.isEmpty(JobData.ownr_ea)
? [
{
//"uuid": "",
address: JobData.ownr_ea.toUpperCase(),
type: "PERSONAL"
// "doNotEmailSource": "",
// "doNotEmail": false,
// "isPreferred": true,
// "transactionEmailNotificationOptIn": false,
// "optInRequestDate": null,
// "optInDate": null
}
]
{
//"uuid": "",
address: JobData.ownr_ea.toUpperCase(),
type: "PERSONAL"
// "doNotEmailSource": "",
// "doNotEmail": false,
// "isPreferred": true,
// "transactionEmailNotificationOptIn": false,
// "optInRequestDate": null,
// "optInDate": null
}
]
: [])
// {
// "uuid": "",
@@ -691,9 +688,9 @@ async function InsertDmsVehicle({ socket, redisHelpers, JobData, txEnvelope, DMS
txEnvelope.dms_unsold === true
? ""
: moment(txEnvelope.inservicedate)
//.tz(JobData.bodyshop.timezone)
.startOf("day")
.toISOString()
//.tz(JobData.bodyshop.timezone)
.startOf("day")
.toISOString()
}),
//"lastServiceDate": "2011-11-23",
vehicleId: DMSVid.vehiclesVehId
@@ -735,8 +732,8 @@ async function InsertDmsVehicle({ socket, redisHelpers, JobData, txEnvelope, DMS
txEnvelope.dms_unsold === true
? ""
: moment()
// .tz(JobData.bodyshop.timezone)
.format("YYYY-MM-DD"),
// .tz(JobData.bodyshop.timezone)
.format("YYYY-MM-DD"),
// "deliveryMileage": 4,
// "doorsQuantity": 4,
// "engineNumber": "",
@@ -753,8 +750,8 @@ async function InsertDmsVehicle({ socket, redisHelpers, JobData, txEnvelope, DMS
: String(JobData.plate_no).replace(/([^\w]|_)/g, "").length === 0
? null
: String(JobData.plate_no)
.replace(/([^\w]|_)/g, "")
.toUpperCase(),
.replace(/([^\w]|_)/g, "")
.toUpperCase(),
make: txEnvelope.dms_make,
// "model": "CC10753",
modelAbrev: txEnvelope.dms_model,
@@ -900,13 +897,13 @@ async function UpdateDmsVehicle({ socket, redisHelpers, JobData, DMSVeh, DMSCust
},
...(oldOwner
? [
{
id: {
assigningPartyId: "PREVIOUS",
value: oldOwner.id.value
{
id: {
assigningPartyId: "PREVIOUS",
value: oldOwner.id.value
}
}
}
]
]
: [])
];
}
@@ -936,24 +933,24 @@ async function UpdateDmsVehicle({ socket, redisHelpers, JobData, DMSVeh, DMSCust
txEnvelope.dms_unsold === true
? ""
: moment(DMSVehToSend.dealer.inServiceDate || txEnvelope.inservicedate)
// .tz(JobData.bodyshop.timezone)
.toISOString()
// .tz(JobData.bodyshop.timezone)
.toISOString()
})
},
vehicle: {
...DMSVehToSend.vehicle,
...(txEnvelope.dms_model_override
? {
make: txEnvelope.dms_make,
modelAbrev: txEnvelope.dms_model
}
make: txEnvelope.dms_make,
modelAbrev: txEnvelope.dms_model
}
: {}),
deliveryDate:
txEnvelope.dms_unsold === true
? ""
: moment(DMSVehToSend.vehicle.deliveryDate)
//.tz(JobData.bodyshop.timezone)
.toISOString()
//.tz(JobData.bodyshop.timezone)
.toISOString()
},
owners: ids
}
@@ -1061,7 +1058,7 @@ async function InsertDmsStartWip({ socket, redisHelpers, JobData }) {
// transID: "",
// userID: "partprgm",
// userName: "PROGRAM, PARTNER"
},
}
});
return result;
} catch (error) {
@@ -1091,9 +1088,9 @@ async function GenerateTransWips({ socket, redisHelpers, JobData }) {
acct: alloc.profitCenter.dms_acctnumber,
cntl:
alloc.profitCenter.dms_control_override &&
alloc.profitCenter.dms_control_override !== null &&
alloc.profitCenter.dms_control_override !== undefined &&
alloc.profitCenter.dms_control_override?.trim() !== ""
alloc.profitCenter.dms_control_override !== null &&
alloc.profitCenter.dms_control_override !== undefined &&
alloc.profitCenter.dms_control_override?.trim() !== ""
? alloc.profitCenter.dms_control_override
: JobData.ro_number,
cntl2: null,
@@ -1114,9 +1111,9 @@ async function GenerateTransWips({ socket, redisHelpers, JobData }) {
acct: alloc.costCenter.dms_acctnumber,
cntl:
alloc.costCenter.dms_control_override &&
alloc.costCenter.dms_control_override !== null &&
alloc.costCenter.dms_control_override !== undefined &&
alloc.costCenter.dms_control_override?.trim() !== ""
alloc.costCenter.dms_control_override !== null &&
alloc.costCenter.dms_control_override !== undefined &&
alloc.costCenter.dms_control_override?.trim() !== ""
? alloc.costCenter.dms_control_override
: JobData.ro_number,
cntl2: null,
@@ -1134,9 +1131,9 @@ async function GenerateTransWips({ socket, redisHelpers, JobData }) {
acct: alloc.costCenter.dms_wip_acctnumber,
cntl:
alloc.costCenter.dms_control_override &&
alloc.costCenter.dms_control_override !== null &&
alloc.costCenter.dms_control_override !== undefined &&
alloc.costCenter.dms_control_override?.trim() !== ""
alloc.costCenter.dms_control_override !== null &&
alloc.costCenter.dms_control_override !== undefined &&
alloc.costCenter.dms_control_override?.trim() !== ""
? alloc.costCenter.dms_control_override
: JobData.ro_number,
cntl2: null,
@@ -1158,9 +1155,9 @@ async function GenerateTransWips({ socket, redisHelpers, JobData }) {
acct: alloc.profitCenter.dms_acctnumber,
cntl:
alloc.profitCenter.dms_control_override &&
alloc.profitCenter.dms_control_override !== null &&
alloc.profitCenter.dms_control_override !== undefined &&
alloc.profitCenter.dms_control_override?.trim() !== ""
alloc.profitCenter.dms_control_override !== null &&
alloc.profitCenter.dms_control_override !== undefined &&
alloc.profitCenter.dms_control_override?.trim() !== ""
? alloc.profitCenter.dms_control_override
: JobData.ro_number,
cntl2: null,
@@ -1228,7 +1225,7 @@ async function PostDmsBatchWip({ socket, redisHelpers, JobData }) {
opCode: "P",
transID: DMSTransHeader.transID,
transWipReqList: await GenerateTransWips({ socket, redisHelpers, JobData })
},
}
});
return result;
} catch (error) {
@@ -1256,7 +1253,7 @@ async function QueryDmsErrWip({ socket, redisHelpers, JobData }) {
socket,
jobid: JobData.id,
requestPathParams: DMSTransHeader.transID,
body: {},
body: {}
});
return result;
} catch (error) {
@@ -1286,7 +1283,7 @@ async function DeleteDmsWip({ socket, redisHelpers, JobData }) {
body: {
opCode: "D",
transID: DMSTransHeader.transID
},
}
});
return result;
} catch (error) {
@@ -1298,9 +1295,15 @@ async function DeleteDmsWip({ socket, redisHelpers, JobData }) {
}
}
async function MarkJobExported({ socket, jobid, JobData }) {
async function MarkJobExported({ socket, jobid, JobData, redisHelpers }) {
CreateFortellisLogEvent(socket, "DEBUG", `Marking job as exported for id ${jobid}`);
const transwips = await redisHelpers.getSessionTransactionData(
socket.id,
getTransactionType(JobData.id),
FortellisCacheEnums.transWips
);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const currentToken =
(socket?.data && socket.data.authToken) || (socket?.handshake?.auth && socket.handshake.auth.token);
@@ -1318,7 +1321,7 @@ async function MarkJobExported({ socket, jobid, JobData }) {
jobid: jobid,
successful: true,
useremail: socket.user.email,
metadata: socket.transWips
metadata: transwips
},
bill: {
exported: true,
@@ -1338,13 +1341,15 @@ async function InsertFailedExportLog({ socket, JobData, error }) {
const result = await client
.setHeaders({ Authorization: `Bearer ${currentToken}` })
.request(queries.INSERT_EXPORT_LOG, {
logs: [{
bodyshopid: JobData.bodyshop.id,
jobid: JobData.id,
successful: false,
message: JSON.stringify(error),
useremail: socket.user.email
}]
logs: [
{
bodyshopid: JobData.bodyshop.id,
jobid: JobData.id,
successful: false,
message: JSON.stringify(error),
useremail: socket.user.email
}
]
});
return result;