1807 lines
76 KiB
JavaScript
1807 lines
76 KiB
JavaScript
import { DeleteFilled } from "@ant-design/icons";
|
|
import { Button, Col, Form, Input, InputNumber, Row, Select, Space, Switch } from "antd";
|
|
import { useTranslation } from "react-i18next";
|
|
import { connect } from "react-redux";
|
|
import { createStructuredSelector } from "reselect";
|
|
import FeatureWrapper from "../feature-wrapper/feature-wrapper.component";
|
|
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
|
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
|
import PhoneFormItem, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
|
|
import FormItemUrl from "../form-items-formatted/url-form-item.component";
|
|
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
|
import { buildSectionActionButton, renderListOrEmpty } from "../layout-form-row/config-list-actions.utils.jsx";
|
|
import InlineValidatedFormRow from "../layout-form-row/inline-validated-form-row.component.jsx";
|
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
|
import { bodyshopHasDmsKey, DMS_MAP, getDmsMode } from "../../utils/dmsUtils.js";
|
|
import {
|
|
INLINE_TITLE_GROUP_STYLE,
|
|
INLINE_TITLE_HANDLE_STYLE,
|
|
INLINE_TITLE_INPUT_STYLE,
|
|
INLINE_TITLE_LABEL_STYLE,
|
|
INLINE_TITLE_ROW_STYLE,
|
|
INLINE_TITLE_SEPARATOR_STYLE,
|
|
INLINE_TITLE_SWITCH_GROUP_STYLE,
|
|
InlineTitleListIcon
|
|
} from "../layout-form-row/inline-form-row-title.utils.js";
|
|
|
|
const timeZonesList = Intl.supportedValuesOf("timeZone");
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
bodyshop: selectBodyshop
|
|
});
|
|
const mapDispatchToProps = () => ({
|
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
|
});
|
|
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoGeneral);
|
|
|
|
export function ShopInfoGeneral({ form, bodyshop }) {
|
|
const { t } = useTranslation();
|
|
const insuranceCompanies = Form.useWatch(["md_ins_cos"], form) || [];
|
|
const duplicateInsuranceCompanyIndexes = getDuplicateIndexSetByNormalizedName(insuranceCompanies, "name");
|
|
const hasDMSKey = bodyshop ? bodyshopHasDmsKey(bodyshop) : false;
|
|
const dmsMode = bodyshop ? getDmsMode(bodyshop, "off") : "none";
|
|
const isReynoldsMode = hasDMSKey && dmsMode === DMS_MAP.reynolds;
|
|
|
|
return (
|
|
<div>
|
|
<LayoutFormRow header={t("bodyshop.labels.businessinformation")} id="businessinformation">
|
|
<Form.Item
|
|
label={t("bodyshop.fields.shopname")}
|
|
name="shopname"
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("bodyshop.fields.address1")}
|
|
name="address1"
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item label={t("bodyshop.fields.address2")} name="address2">
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("bodyshop.fields.city")}
|
|
name="city"
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("bodyshop.fields.state")}
|
|
name="state"
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item label={t("bodyshop.fields.zip_post")} name="zip_post">
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item label={t("bodyshop.fields.country")} name="country">
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item label={t("bodyshop.fields.email")} name="email">
|
|
<FormItemEmail />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("bodyshop.fields.phone")}
|
|
name="phone"
|
|
rules={[({ getFieldValue }) => PhoneItemFormatterValidation(getFieldValue, "phone")]}
|
|
>
|
|
<PhoneFormItem formatDisplayOnly showPhoneAction />
|
|
</Form.Item>
|
|
<Form.Item label={t("bodyshop.fields.website")} name="website">
|
|
<FormItemUrl />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("bodyshop.fields.timezone")}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
name="timezone"
|
|
>
|
|
<Select
|
|
showSearch
|
|
options={timeZonesList.map((z) => {
|
|
return { label: z, value: z };
|
|
})}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item label={t("bodyshop.fields.insurance_vendor_id")} name="insurance_vendor_id">
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item label={t("bodyshop.fields.logo_img_path")} name={["logo_img_path", "src"]}>
|
|
<FormItemUrl />
|
|
</Form.Item>
|
|
<Form.Item label={t("bodyshop.fields.logo_img_path_height")} name={["logo_img_path", "height"]}>
|
|
<Input suffix="px" />
|
|
</Form.Item>
|
|
<Form.Item label={t("bodyshop.fields.logo_img_path_width")} name={["logo_img_path", "width"]}>
|
|
<Input suffix="px" />
|
|
</Form.Item>
|
|
<Form.Item label={t("bodyshop.fields.logo_img_header_margin")} name={["logo_img_path", "headerMargin"]}>
|
|
<InputNumber min={0} suffix="px" />
|
|
</Form.Item>
|
|
<Form.Item label={t("bodyshop.fields.logo_img_footer_margin")} name={["logo_img_path", "footerMargin"]}>
|
|
<InputNumber min={0} suffix="px" />
|
|
</Form.Item>
|
|
</LayoutFormRow>
|
|
<FeatureWrapper featureName="scoreboard" noauth={() => null}>
|
|
<LayoutFormRow
|
|
id="scoreboardsetup"
|
|
title={
|
|
<div
|
|
style={{
|
|
...INLINE_TITLE_ROW_STYLE,
|
|
justifyContent: "space-between"
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
fontWeight: 500,
|
|
marginRight: "auto"
|
|
}}
|
|
>
|
|
{t("bodyshop.labels.scoreboardsetup")}
|
|
</div>
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: 4,
|
|
flexWrap: "wrap",
|
|
marginLeft: "auto"
|
|
}}
|
|
>
|
|
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
|
|
<div style={INLINE_TITLE_SWITCH_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>
|
|
{t("bodyshop.fields.scoreboard_setup.ignore_blocked_days")}
|
|
</div>
|
|
<Form.Item noStyle name={["scoreboard_target", "ignoreblockeddays"]} valuePropName="checked">
|
|
<Switch />
|
|
</Form.Item>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
wrapTitle
|
|
>
|
|
<Form.Item
|
|
label={t("bodyshop.fields.scoreboard_setup.daily_paint_target")}
|
|
name={["scoreboard_target", "dailyPaintTarget"]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<InputNumber min={0} precision={0} />
|
|
</Form.Item>
|
|
|
|
<Form.Item
|
|
label={t("bodyshop.fields.scoreboard_setup.daily_body_target")}
|
|
name={["scoreboard_target", "dailyBodyTarget"]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<InputNumber min={0} precision={0} />
|
|
</Form.Item>
|
|
|
|
<Form.Item
|
|
label={t("bodyshop.fields.scoreboard_setup.last_number_working_days")}
|
|
name={["scoreboard_target", "lastNumberWorkingDays"]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<InputNumber min={0} max={12} precision={0} suffix="days" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("bodyshop.fields.scoreboard_setup.production_target_hours")}
|
|
name={["prodtargethrs"]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<InputNumber min={1} precision={1} suffix="hrs" />
|
|
</Form.Item>
|
|
</LayoutFormRow>
|
|
</FeatureWrapper>
|
|
<LayoutFormRow header={t("bodyshop.labels.systemsettings")} id="systemsettings">
|
|
<>
|
|
<Form.Item
|
|
key="md_categories"
|
|
name={["md_categories"]}
|
|
label={t("bodyshop.fields.md_categories")}
|
|
rules={[
|
|
{
|
|
//message: t("general.validation.required"),
|
|
type: "array"
|
|
}
|
|
]}
|
|
>
|
|
<Select mode="tags" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
key="md_referral_sources"
|
|
name={["md_referral_sources"]}
|
|
label={t("bodyshop.fields.md_referral_sources")}
|
|
rules={[
|
|
{
|
|
required: true,
|
|
//message: t("general.validation.required"),
|
|
type: "array"
|
|
}
|
|
]}
|
|
>
|
|
<Select mode="tags" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
key="md_ded_notes"
|
|
name={["md_ded_notes"]}
|
|
label={t("bodyshop.fields.md_ded_notes")}
|
|
rules={[
|
|
{
|
|
//message: t("general.validation.required"),
|
|
type: "array"
|
|
}
|
|
]}
|
|
>
|
|
<Select mode="tags" />
|
|
</Form.Item>
|
|
|
|
<Row gutter={[16, 0]} wrap>
|
|
<Col xs={24} sm={12} xl={8}>
|
|
<Form.Item
|
|
key="enforce_referral"
|
|
name={["enforce_referral"]}
|
|
label={t("bodyshop.fields.enforce_referral")}
|
|
valuePropName="checked"
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
</Col>
|
|
<Col xs={24} sm={12} xl={8}>
|
|
<Form.Item
|
|
key="enforce_conversion_csr"
|
|
name={["enforce_conversion_csr"]}
|
|
label={t("bodyshop.fields.enforce_conversion_csr")}
|
|
valuePropName="checked"
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
</Col>
|
|
<Col xs={24} sm={12} xl={8}>
|
|
<Form.Item
|
|
key="enforce_conversion_category"
|
|
name={["enforce_conversion_category"]}
|
|
label={t("bodyshop.fields.enforce_conversion_category")}
|
|
valuePropName="checked"
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
</Col>
|
|
<Col xs={24} sm={12} xl={8}>
|
|
<Form.Item
|
|
key="use_fippa"
|
|
label={t("bodyshop.fields.use_fippa")}
|
|
name={["use_fippa"]}
|
|
valuePropName="checked"
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
</Col>
|
|
<Col xs={24} sm={12} xl={8}>
|
|
<Form.Item
|
|
key="parts_queue_toggle"
|
|
label={t("bodyshop.fields.md_functionality_toggles.parts_queue_toggle")}
|
|
name={["md_functionality_toggles", "parts_queue_toggle"]}
|
|
valuePropName="checked"
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
</Col>
|
|
<Col xs={24} sm={12} xl={8}>
|
|
<Form.Item
|
|
key="last_name_first"
|
|
name={["last_name_first"]}
|
|
label={t("bodyshop.fields.last_name_first")}
|
|
valuePropName="checked"
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
</Col>
|
|
</Row>
|
|
|
|
<div style={{ display: "grid", gap: 16, marginTop: 16 }}>
|
|
<LayoutFormRow
|
|
grow
|
|
style={{ marginBottom: 0 }}
|
|
title={
|
|
<div
|
|
style={{
|
|
...INLINE_TITLE_ROW_STYLE,
|
|
justifyContent: "space-between"
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
fontWeight: 500,
|
|
marginRight: "auto"
|
|
}}
|
|
>
|
|
{t("bodyshop.labels.localmediaserver")}
|
|
</div>
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: 4,
|
|
flexWrap: "wrap",
|
|
marginLeft: "auto"
|
|
}}
|
|
>
|
|
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
|
|
<div style={INLINE_TITLE_SWITCH_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>
|
|
{t("bodyshop.fields.system_settings.local_media_server.enabled")}
|
|
</div>
|
|
<Form.Item noStyle name={["uselocalmediaserver"]} valuePropName="checked">
|
|
<Switch />
|
|
</Form.Item>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
wrapTitle
|
|
>
|
|
<Form.Item
|
|
key="localmediaserverhttp"
|
|
name={["localmediaserverhttp"]}
|
|
label={t("bodyshop.fields.system_settings.local_media_server.http_path")}
|
|
>
|
|
<FormItemUrl />
|
|
</Form.Item>
|
|
<Form.Item
|
|
key="localmediaservernetwork"
|
|
name={["localmediaservernetwork"]}
|
|
label={t("bodyshop.fields.system_settings.local_media_server.network_path")}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item
|
|
key="localmediatoken"
|
|
name={["localmediatoken"]}
|
|
label={t("bodyshop.fields.system_settings.local_media_server.token")}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
</LayoutFormRow>
|
|
|
|
<LayoutFormRow header={t("bodyshop.labels.autoemail")} grow style={{ marginBottom: 0 }}>
|
|
<Form.Item
|
|
key="attach_pdf_to_email"
|
|
name={["attach_pdf_to_email"]}
|
|
label={t("bodyshop.fields.system_settings.auto_email.attach_pdf_to_email")}
|
|
valuePropName="checked"
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
<Form.Item
|
|
key="md_from_emails"
|
|
name={["md_from_emails"]}
|
|
label={t("bodyshop.fields.system_settings.auto_email.from_emails")}
|
|
>
|
|
<Select mode="tags" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
key="md_email_cc_parts_order"
|
|
name={["md_email_cc", "parts_order"]}
|
|
label={t("bodyshop.fields.system_settings.auto_email.parts_order_cc")}
|
|
rules={[
|
|
{
|
|
//message: t("general.validation.required"),
|
|
type: "array"
|
|
}
|
|
]}
|
|
>
|
|
<Select mode="tags" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
key="md_email_cc_parts_return_slip"
|
|
name={["md_email_cc", "parts_return_slip"]}
|
|
label={t("bodyshop.fields.system_settings.auto_email.parts_return_slip_cc")}
|
|
rules={[
|
|
{
|
|
//message: t("general.validation.required"),
|
|
type: "array"
|
|
}
|
|
]}
|
|
>
|
|
<Select mode="tags" />
|
|
</Form.Item>
|
|
</LayoutFormRow>
|
|
|
|
<LayoutFormRow
|
|
grow
|
|
style={{ marginBottom: 0 }}
|
|
title={
|
|
<div
|
|
style={{
|
|
...INLINE_TITLE_ROW_STYLE,
|
|
justifyContent: "space-between"
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
fontWeight: 500,
|
|
marginRight: "auto"
|
|
}}
|
|
>
|
|
{t("bodyshop.labels.jobcosting")}
|
|
</div>
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: 4,
|
|
flexWrap: "wrap",
|
|
marginLeft: "auto"
|
|
}}
|
|
>
|
|
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
|
|
<div style={INLINE_TITLE_SWITCH_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>
|
|
{t("bodyshop.fields.system_settings.job_costing.use_paint_scale_data")}
|
|
</div>
|
|
<Form.Item
|
|
noStyle
|
|
key="use_paint_scale_data"
|
|
name={["use_paint_scale_data"]}
|
|
valuePropName="checked"
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
wrapTitle
|
|
>
|
|
<Form.Item
|
|
key="target_touchtime"
|
|
name={["target_touchtime"]}
|
|
label={t("bodyshop.fields.system_settings.job_costing.target_touch_time")}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<InputNumber min={0.1} precision={1} />
|
|
</Form.Item>
|
|
<Form.Item
|
|
key="md_hour_split_prep"
|
|
label={t("bodyshop.fields.system_settings.job_costing.prep_hour_split")}
|
|
name={["md_hour_split", "prep"]}
|
|
dependencies={[["md_hour_split", "paint"]]}
|
|
rules={[
|
|
({ getFieldValue }) => ({
|
|
validator(rule, value) {
|
|
if (!value && !getFieldValue(["md_hour_split", "paint"])) {
|
|
return Promise.resolve();
|
|
}
|
|
if (value + getFieldValue(["md_hour_split", "paint"]) === 1) {
|
|
return Promise.resolve();
|
|
}
|
|
return Promise.reject(t("bodyshop.validation.larsplit"));
|
|
}
|
|
})
|
|
]}
|
|
>
|
|
<InputNumber min={0} max={1} precision={2} />
|
|
</Form.Item>
|
|
<Form.Item
|
|
key="md_hour_split_paint"
|
|
label={t("bodyshop.fields.system_settings.job_costing.paint_hour_split")}
|
|
name={["md_hour_split", "paint"]}
|
|
dependencies={[["md_hour_split", "prep"]]}
|
|
rules={[
|
|
({ getFieldValue }) => ({
|
|
validator(rule, value) {
|
|
if (!value && !getFieldValue(["md_hour_split", "paint"])) {
|
|
return Promise.resolve();
|
|
}
|
|
if (value + getFieldValue(["md_hour_split", "prep"]) === 1) {
|
|
return Promise.resolve();
|
|
}
|
|
return Promise.reject(t("bodyshop.validation.larsplit"));
|
|
}
|
|
})
|
|
]}
|
|
>
|
|
<InputNumber min={0} max={1} precision={2} />
|
|
</Form.Item>
|
|
<Form.Item
|
|
key="jc_hourly_rates_mapa"
|
|
label={t("bodyshop.fields.system_settings.job_costing.paint_materials_hourly_cost_rate")}
|
|
name={["jc_hourly_rates", "mapa"]}
|
|
>
|
|
<CurrencyInput prefix="$" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
key="jc_hourly_rates_mash"
|
|
label={t("bodyshop.fields.system_settings.job_costing.shop_materials_hourly_cost_rate")}
|
|
name={["jc_hourly_rates", "mash"]}
|
|
>
|
|
<CurrencyInput prefix="$" />
|
|
</Form.Item>
|
|
</LayoutFormRow>
|
|
|
|
<LayoutFormRow
|
|
header={t("bodyshop.labels.shop_enabled_features")}
|
|
id="sharing"
|
|
grow
|
|
style={{ marginBottom: 0 }}
|
|
>
|
|
<Form.Item
|
|
label={t("general.actions.sharetoteams")}
|
|
valuePropName="checked"
|
|
name={["md_functionality_toggles", "teams"]}
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
{isReynoldsMode && (
|
|
<Form.Item
|
|
initialValue
|
|
label={t("bodyshop.fields.md_functionality_toggles.enhanced_early_ros")}
|
|
name={["md_functionality_toggles", "enhanced_early_ros"]}
|
|
valuePropName="checked"
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
)}
|
|
</LayoutFormRow>
|
|
</div>
|
|
</>
|
|
</LayoutFormRow>
|
|
<Form.List name={["md_messaging_presets"]}>
|
|
{(fields, { add, remove, move }) => {
|
|
return (
|
|
<LayoutFormRow
|
|
grow
|
|
header={t("bodyshop.labels.messagingpresets")}
|
|
id="messagingpresets"
|
|
actions={[
|
|
buildSectionActionButton("add-messaging-preset", t("bodyshop.actions.add_messaging_preset"), () => {
|
|
add();
|
|
})
|
|
]}
|
|
>
|
|
<div>
|
|
{renderListOrEmpty(fields, t("bodyshop.actions.add_messaging_preset"), () =>
|
|
fields.map((field, index) => {
|
|
return (
|
|
<Form.Item noStyle key={field.key}>
|
|
<InlineValidatedFormRow
|
|
form={form}
|
|
errorNames={[["md_messaging_presets", field.name, "label"]]}
|
|
noDivider
|
|
title={
|
|
<div style={INLINE_TITLE_ROW_STYLE}>
|
|
<InlineTitleListIcon style={INLINE_TITLE_HANDLE_STYLE} />
|
|
<div style={INLINE_TITLE_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.messaginglabel_short")}</div>
|
|
<Form.Item
|
|
noStyle
|
|
name={[field.name, "label"]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<Input
|
|
size="small"
|
|
placeholder={t("bodyshop.fields.messaginglabel_short")}
|
|
style={{
|
|
...INLINE_TITLE_INPUT_STYLE,
|
|
width: "100%"
|
|
}}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
</div>
|
|
}
|
|
extra={
|
|
<Space align="center" size="small">
|
|
<Button
|
|
type="text"
|
|
danger
|
|
icon={<DeleteFilled />}
|
|
onClick={() => {
|
|
remove(field.name);
|
|
}}
|
|
/>
|
|
<FormListMoveArrows
|
|
move={move}
|
|
index={index}
|
|
total={fields.length}
|
|
orientation="horizontal"
|
|
/>
|
|
</Space>
|
|
}
|
|
>
|
|
<Form.Item
|
|
label={t("bodyshop.fields.messagingtext_short")}
|
|
name={[field.name, "text"]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
style={{ marginBottom: 0 }}
|
|
>
|
|
<Input.TextArea
|
|
autoSize={{ minRows: 1, maxRows: 3 }}
|
|
placeholder={t("bodyshop.fields.messagingtext_short")}
|
|
/>
|
|
</Form.Item>
|
|
</InlineValidatedFormRow>
|
|
</Form.Item>
|
|
);
|
|
})
|
|
)}
|
|
</div>
|
|
</LayoutFormRow>
|
|
);
|
|
}}
|
|
</Form.List>
|
|
<Form.List name={["md_notes_presets"]}>
|
|
{(fields, { add, remove, move }) => {
|
|
return (
|
|
<LayoutFormRow
|
|
grow
|
|
header={t("bodyshop.labels.notespresets")}
|
|
id="notespresets"
|
|
actions={[
|
|
buildSectionActionButton("add-note-preset", t("bodyshop.actions.add_note_preset"), () => {
|
|
add();
|
|
})
|
|
]}
|
|
>
|
|
<div>
|
|
{renderListOrEmpty(fields, t("bodyshop.actions.add_note_preset"), () =>
|
|
fields.map((field, index) => {
|
|
return (
|
|
<Form.Item noStyle key={field.key}>
|
|
<InlineValidatedFormRow
|
|
form={form}
|
|
errorNames={[["md_notes_presets", field.name, "label"]]}
|
|
noDivider
|
|
title={
|
|
<div style={INLINE_TITLE_ROW_STYLE}>
|
|
<InlineTitleListIcon style={INLINE_TITLE_HANDLE_STYLE} />
|
|
<div style={INLINE_TITLE_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.noteslabel_short")}</div>
|
|
<Form.Item
|
|
noStyle
|
|
name={[field.name, "label"]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<Input
|
|
size="small"
|
|
placeholder={t("bodyshop.fields.noteslabel_short")}
|
|
style={{
|
|
...INLINE_TITLE_INPUT_STYLE,
|
|
width: "100%"
|
|
}}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
</div>
|
|
}
|
|
extra={
|
|
<Space align="center" size="small">
|
|
<Button
|
|
type="text"
|
|
danger
|
|
icon={<DeleteFilled />}
|
|
onClick={() => {
|
|
remove(field.name);
|
|
}}
|
|
/>
|
|
<FormListMoveArrows
|
|
move={move}
|
|
index={index}
|
|
total={fields.length}
|
|
orientation="horizontal"
|
|
/>
|
|
</Space>
|
|
}
|
|
>
|
|
<Form.Item
|
|
label={t("bodyshop.fields.notestext_short")}
|
|
name={[field.name, "text"]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
style={{ marginBottom: 0 }}
|
|
>
|
|
<Input.TextArea
|
|
autoSize={{ minRows: 1, maxRows: 3 }}
|
|
placeholder={t("bodyshop.fields.notestext_short")}
|
|
/>
|
|
</Form.Item>
|
|
</InlineValidatedFormRow>
|
|
</Form.Item>
|
|
);
|
|
})
|
|
)}
|
|
</div>
|
|
</LayoutFormRow>
|
|
);
|
|
}}
|
|
</Form.List>
|
|
<Form.List name={["md_parts_locations"]}>
|
|
{(fields, { add, remove, move }) => {
|
|
return (
|
|
<LayoutFormRow
|
|
grow
|
|
header={t("bodyshop.labels.partslocations")}
|
|
id="partslocations"
|
|
actions={[
|
|
buildSectionActionButton("add-parts-location", t("bodyshop.actions.addpartslocation"), () => {
|
|
add();
|
|
})
|
|
]}
|
|
>
|
|
<div>
|
|
{renderListOrEmpty(fields, t("bodyshop.actions.addpartslocation"), () =>
|
|
fields.map((field, index) => {
|
|
return (
|
|
<Form.Item noStyle key={field.key}>
|
|
<InlineValidatedFormRow
|
|
form={form}
|
|
errorNames={[["md_parts_locations", field.name]]}
|
|
noDivider
|
|
title={
|
|
<div style={INLINE_TITLE_ROW_STYLE}>
|
|
<InlineTitleListIcon style={INLINE_TITLE_HANDLE_STYLE} />
|
|
<div style={INLINE_TITLE_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.partslocation")}</div>
|
|
<Form.Item
|
|
noStyle
|
|
name={[field.name]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<Input
|
|
size="small"
|
|
placeholder={t("bodyshop.fields.partslocation")}
|
|
style={{
|
|
...INLINE_TITLE_INPUT_STYLE,
|
|
width: "100%"
|
|
}}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
</div>
|
|
}
|
|
extra={
|
|
<Space align="center" size="small">
|
|
<Button
|
|
type="text"
|
|
danger
|
|
icon={<DeleteFilled />}
|
|
onClick={() => {
|
|
remove(field.name);
|
|
}}
|
|
/>
|
|
<FormListMoveArrows
|
|
move={move}
|
|
index={index}
|
|
total={fields.length}
|
|
orientation="horizontal"
|
|
/>
|
|
</Space>
|
|
}
|
|
/>
|
|
</Form.Item>
|
|
);
|
|
})
|
|
)}
|
|
</div>
|
|
</LayoutFormRow>
|
|
);
|
|
}}
|
|
</Form.List>
|
|
|
|
{/*Start Insurance Provider Row */}
|
|
<Form.List
|
|
name={["md_ins_cos"]}
|
|
rules={[
|
|
{
|
|
validator: async (_, companies = []) => {
|
|
const normalizedNames = (Array.isArray(companies) ? companies : [])
|
|
.map((company) => (company?.name ?? "").toString().trim().toLowerCase())
|
|
.filter(Boolean);
|
|
|
|
if (new Set(normalizedNames).size !== normalizedNames.length) {
|
|
throw new Error(t("bodyshop.errors.duplicate_insurance_company"));
|
|
}
|
|
}
|
|
}
|
|
]}
|
|
>
|
|
{(fields, { add, remove, move }) => {
|
|
return (
|
|
<LayoutFormRow
|
|
grow
|
|
header={<span id="insurancecos-header">{t("bodyshop.labels.insurancecos")}</span>}
|
|
id="insurancecos"
|
|
actions={[
|
|
buildSectionActionButton(
|
|
"add-insurance-company",
|
|
t("bodyshop.actions.add_insurance_company"),
|
|
() => {
|
|
add();
|
|
},
|
|
"insurancecos-add-button"
|
|
)
|
|
]}
|
|
>
|
|
<div>
|
|
{renderListOrEmpty(fields, t("bodyshop.actions.add_insurance_company"), () =>
|
|
fields.map((field, index) => {
|
|
const insuranceCompanyErrors = duplicateInsuranceCompanyIndexes.has(field.name)
|
|
? [t("bodyshop.errors.duplicate_insurance_company")]
|
|
: [];
|
|
|
|
return (
|
|
<Form.Item noStyle key={field.key}>
|
|
<InlineValidatedFormRow
|
|
form={form}
|
|
errorNames={[["md_ins_cos", field.name, "name"]]}
|
|
extraErrors={insuranceCompanyErrors}
|
|
noDivider
|
|
title={
|
|
<div style={INLINE_TITLE_ROW_STYLE}>
|
|
<InlineTitleListIcon style={INLINE_TITLE_HANDLE_STYLE} />
|
|
<div style={INLINE_TITLE_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.md_ins_co.name")}</div>
|
|
<Form.Item
|
|
noStyle
|
|
name={[field.name, "name"]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<Input
|
|
size="small"
|
|
placeholder={t("bodyshop.fields.md_ins_co.name")}
|
|
style={{
|
|
...INLINE_TITLE_INPUT_STYLE,
|
|
width: "100%"
|
|
}}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
|
|
<div style={INLINE_TITLE_SWITCH_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.md_ins_co.private")}</div>
|
|
<Form.Item noStyle name={[field.name, "private"]} valuePropName="checked">
|
|
<Switch size="small" />
|
|
</Form.Item>
|
|
</div>
|
|
</div>
|
|
}
|
|
wrapTitle
|
|
extra={
|
|
<Space align="center" size="small">
|
|
<Button
|
|
type="text"
|
|
danger
|
|
icon={<DeleteFilled />}
|
|
onClick={() => {
|
|
remove(field.name);
|
|
}}
|
|
/>
|
|
<FormListMoveArrows
|
|
move={move}
|
|
index={index}
|
|
total={fields.length}
|
|
orientation="horizontal"
|
|
/>
|
|
</Space>
|
|
}
|
|
>
|
|
<Form.Item
|
|
label={t("bodyshop.fields.md_ins_co.street1")}
|
|
key={`${index}street1`}
|
|
name={[field.name, "street1"]}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("bodyshop.fields.md_ins_co.street2")}
|
|
key={`${index}street2`}
|
|
name={[field.name, "street2"]}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("bodyshop.fields.md_ins_co.city")}
|
|
key={`${index}city`}
|
|
name={[field.name, "city"]}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("bodyshop.fields.md_ins_co.state")}
|
|
key={`${index}state`}
|
|
name={[field.name, "state"]}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("bodyshop.fields.md_ins_co.zip")}
|
|
key={`${index}zip`}
|
|
name={[field.name, "zip"]}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
</InlineValidatedFormRow>
|
|
</Form.Item>
|
|
);
|
|
})
|
|
)}
|
|
</div>
|
|
</LayoutFormRow>
|
|
);
|
|
}}
|
|
</Form.List>
|
|
{/*End Insurance Provider Row */}
|
|
|
|
<Form.List name={["md_estimators"]}>
|
|
{(fields, { add, remove, move }) => {
|
|
return (
|
|
<LayoutFormRow
|
|
grow
|
|
header={t("bodyshop.labels.estimators")}
|
|
id="estimators"
|
|
actions={[
|
|
buildSectionActionButton("add-estimator", t("bodyshop.actions.add_estimator"), () => {
|
|
add();
|
|
})
|
|
]}
|
|
>
|
|
<div>
|
|
{renderListOrEmpty(fields, t("bodyshop.actions.add_estimator"), () =>
|
|
fields.map((field, index) => {
|
|
return (
|
|
<Form.Item noStyle key={field.key}>
|
|
<InlineValidatedFormRow
|
|
form={form}
|
|
errorNames={[
|
|
["md_estimators", field.name, "est_ct_fn"],
|
|
["md_estimators", field.name, "est_ct_ln"],
|
|
["md_estimators", field.name, "est_co_nm"]
|
|
]}
|
|
noDivider
|
|
title={
|
|
<div style={INLINE_TITLE_ROW_STYLE}>
|
|
<InlineTitleListIcon style={INLINE_TITLE_HANDLE_STYLE} />
|
|
<div style={INLINE_TITLE_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.est_ct_fn_short")}</div>
|
|
<Form.Item noStyle name={[field.name, "est_ct_fn"]}>
|
|
<Input
|
|
size="small"
|
|
placeholder={t("jobs.fields.est_ct_fn_short")}
|
|
style={{
|
|
...INLINE_TITLE_INPUT_STYLE,
|
|
width: "100%"
|
|
}}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
|
|
<div style={INLINE_TITLE_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.est_ct_ln_short")}</div>
|
|
<Form.Item noStyle name={[field.name, "est_ct_ln"]}>
|
|
<Input
|
|
size="small"
|
|
placeholder={t("jobs.fields.est_ct_ln_short")}
|
|
style={{
|
|
...INLINE_TITLE_INPUT_STYLE,
|
|
width: "100%"
|
|
}}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
|
|
<div style={INLINE_TITLE_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.est_co_nm_short")}</div>
|
|
<Form.Item noStyle name={[field.name, "est_co_nm"]}>
|
|
<Input
|
|
size="small"
|
|
placeholder={t("jobs.fields.est_co_nm_short")}
|
|
style={{
|
|
...INLINE_TITLE_INPUT_STYLE,
|
|
width: "100%"
|
|
}}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
</div>
|
|
}
|
|
wrapTitle
|
|
extra={
|
|
<Space align="center" size="small">
|
|
<Button
|
|
type="text"
|
|
danger
|
|
icon={<DeleteFilled />}
|
|
onClick={() => {
|
|
remove(field.name);
|
|
}}
|
|
/>
|
|
<FormListMoveArrows
|
|
move={move}
|
|
index={index}
|
|
total={fields.length}
|
|
orientation="horizontal"
|
|
/>
|
|
</Space>
|
|
}
|
|
>
|
|
<Form.Item
|
|
label={t("jobs.fields.est_ph1_short")}
|
|
key={`${index}est_ph1`}
|
|
name={[field.name, "est_ph1"]}
|
|
rules={[
|
|
({ getFieldValue }) => PhoneItemFormatterValidation(getFieldValue, [field.name, "est_ph"])
|
|
]}
|
|
>
|
|
<PhoneFormItem formatDisplayOnly showPhoneAction />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("jobs.fields.est_ea_short")}
|
|
key={`${index}est_ea`}
|
|
name={[field.name, "est_ea"]}
|
|
rules={[
|
|
{
|
|
type: "email",
|
|
message: "This is not a valid email address."
|
|
}
|
|
]}
|
|
>
|
|
<FormItemEmail email={form.getFieldValue([field.name, "est_ea"])} />
|
|
</Form.Item>
|
|
</InlineValidatedFormRow>
|
|
</Form.Item>
|
|
);
|
|
})
|
|
)}
|
|
</div>
|
|
</LayoutFormRow>
|
|
);
|
|
}}
|
|
</Form.List>
|
|
<Form.List name={["md_filehandlers"]}>
|
|
{(fields, { add, remove, move }) => {
|
|
return (
|
|
<LayoutFormRow
|
|
grow
|
|
header={t("bodyshop.labels.filehandlers")}
|
|
id="filehandlers"
|
|
actions={[
|
|
buildSectionActionButton("add-adjuster", t("bodyshop.actions.add_adjuster"), () => {
|
|
add();
|
|
})
|
|
]}
|
|
>
|
|
<div>
|
|
{renderListOrEmpty(fields, t("bodyshop.actions.add_adjuster"), () =>
|
|
fields.map((field, index) => {
|
|
return (
|
|
<Form.Item noStyle key={field.key}>
|
|
<LayoutFormRow
|
|
noDivider
|
|
title={
|
|
<div style={INLINE_TITLE_ROW_STYLE}>
|
|
<InlineTitleListIcon style={INLINE_TITLE_HANDLE_STYLE} />
|
|
<div style={INLINE_TITLE_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.ins_ct_fn_short")}</div>
|
|
<Form.Item noStyle name={[field.name, "ins_ct_fn"]}>
|
|
<Input
|
|
size="small"
|
|
placeholder={t("jobs.fields.ins_ct_fn_short")}
|
|
style={{
|
|
...INLINE_TITLE_INPUT_STYLE,
|
|
width: "100%"
|
|
}}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
|
|
<div style={INLINE_TITLE_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.ins_ct_ln_short")}</div>
|
|
<Form.Item noStyle name={[field.name, "ins_ct_ln"]}>
|
|
<Input
|
|
size="small"
|
|
placeholder={t("jobs.fields.ins_ct_ln_short")}
|
|
style={{
|
|
...INLINE_TITLE_INPUT_STYLE,
|
|
width: "100%"
|
|
}}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
</div>
|
|
}
|
|
wrapTitle
|
|
extra={
|
|
<Space align="center" size="small">
|
|
<Button
|
|
type="text"
|
|
danger
|
|
icon={<DeleteFilled />}
|
|
onClick={() => {
|
|
remove(field.name);
|
|
}}
|
|
/>
|
|
<FormListMoveArrows
|
|
move={move}
|
|
index={index}
|
|
total={fields.length}
|
|
orientation="horizontal"
|
|
/>
|
|
</Space>
|
|
}
|
|
>
|
|
<Form.Item
|
|
label={t("jobs.fields.ins_ph1_short")}
|
|
key={`${index}ins_ph1`}
|
|
name={[field.name, "ins_ph1"]}
|
|
rules={[
|
|
({ getFieldValue }) => PhoneItemFormatterValidation(getFieldValue, [field.name, "ins_ph"])
|
|
]}
|
|
>
|
|
<PhoneFormItem formatDisplayOnly showPhoneAction />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("jobs.fields.ins_ea_short")}
|
|
key={`${index}ins_ea`}
|
|
name={[field.name, "ins_ea"]}
|
|
rules={[
|
|
{
|
|
type: "email",
|
|
message: "This is not a valid email address."
|
|
}
|
|
]}
|
|
>
|
|
<FormItemEmail email={form.getFieldValue([field.name, "ins_ea"])} />
|
|
</Form.Item>
|
|
</LayoutFormRow>
|
|
</Form.Item>
|
|
);
|
|
})
|
|
)}
|
|
</div>
|
|
</LayoutFormRow>
|
|
);
|
|
}}
|
|
</Form.List>
|
|
<FeatureWrapper featureName="courtesycars" noauth={() => null}>
|
|
<Form.List name={["md_ccc_rates"]}>
|
|
{(fields, { add, remove, move }) => {
|
|
return (
|
|
<LayoutFormRow
|
|
grow
|
|
header={t("bodyshop.fields.md_ccc_rates")}
|
|
id="md_ccc_rates"
|
|
actions={[
|
|
buildSectionActionButton(
|
|
"add-courtesy-car-rate-preset",
|
|
t("bodyshop.actions.add_courtesy_car_rate_preset"),
|
|
() => {
|
|
add();
|
|
}
|
|
)
|
|
]}
|
|
>
|
|
<div>
|
|
{renderListOrEmpty(fields, t("bodyshop.actions.add_courtesy_car_rate_preset"), () =>
|
|
fields.map((field, index) => {
|
|
return (
|
|
<Form.Item noStyle key={field.key}>
|
|
<InlineValidatedFormRow
|
|
form={form}
|
|
errorNames={[["md_ccc_rates", field.name, "label"]]}
|
|
noDivider
|
|
title={
|
|
<div style={INLINE_TITLE_ROW_STYLE}>
|
|
<InlineTitleListIcon style={INLINE_TITLE_HANDLE_STYLE} />
|
|
<div style={INLINE_TITLE_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>{t("general.labels.label")}</div>
|
|
<Form.Item
|
|
noStyle
|
|
name={[field.name, "label"]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<Input
|
|
size="small"
|
|
placeholder={t("general.labels.label")}
|
|
style={{
|
|
...INLINE_TITLE_INPUT_STYLE,
|
|
width: "100%"
|
|
}}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
</div>
|
|
}
|
|
wrapTitle
|
|
extra={
|
|
<Space align="center" size="small">
|
|
<Button
|
|
type="text"
|
|
danger
|
|
icon={<DeleteFilled />}
|
|
onClick={() => {
|
|
remove(field.name);
|
|
}}
|
|
/>
|
|
<FormListMoveArrows
|
|
move={move}
|
|
index={index}
|
|
total={fields.length}
|
|
orientation="horizontal"
|
|
/>
|
|
</Space>
|
|
}
|
|
>
|
|
<Form.Item
|
|
label={t("contracts.fields.actax")}
|
|
key={`${index}actax`}
|
|
name={[field.name, "actax"]}
|
|
>
|
|
<InputNumber precision={2} suffix="%" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("contracts.fields.dailyfreekm")}
|
|
key={`${index}dailyfreekm`}
|
|
name={[field.name, "dailyfreekm"]}
|
|
>
|
|
<InputNumber precision={2} />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("contracts.fields.refuelcharge")}
|
|
key={`${index}refuelcharge`}
|
|
name={[field.name, "refuelcharge"]}
|
|
>
|
|
<CurrencyInput precision={2} min={0} prefix="$" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("contracts.fields.excesskmrate")}
|
|
key={`${index}excesskmrate`}
|
|
name={[field.name, "excesskmrate"]}
|
|
>
|
|
<CurrencyInput precision={2} min={0} prefix="$" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("contracts.fields.cleanupcharge")}
|
|
key={`${index}cleanupcharge`}
|
|
name={[field.name, "cleanupcharge"]}
|
|
>
|
|
<CurrencyInput precision={2} min={0} prefix="$" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("contracts.fields.damagewaiver")}
|
|
key={`${index}damagewaiver`}
|
|
name={[field.name, "damagewaiver"]}
|
|
>
|
|
<CurrencyInput precision={2} min={0} prefix="$" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("contracts.fields.federaltax")}
|
|
key={`${index}federaltax`}
|
|
name={[field.name, "federaltax"]}
|
|
>
|
|
<InputNumber precision={2} suffix="%" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("contracts.fields.statetax")}
|
|
key={`${index}statetax`}
|
|
name={[field.name, "statetax"]}
|
|
>
|
|
<InputNumber precision={2} suffix="%" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("contracts.fields.localtax")}
|
|
key={`${index}localtax`}
|
|
name={[field.name, "localtax"]}
|
|
>
|
|
<InputNumber precision={2} suffix="%" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("contracts.fields.coverage")}
|
|
key={`${index}coverage`}
|
|
name={[field.name, "coverage"]}
|
|
>
|
|
<CurrencyInput precision={2} min={0} prefix="$" />
|
|
</Form.Item>
|
|
</InlineValidatedFormRow>
|
|
</Form.Item>
|
|
);
|
|
})
|
|
)}
|
|
</div>
|
|
</LayoutFormRow>
|
|
);
|
|
}}
|
|
</Form.List>
|
|
</FeatureWrapper>
|
|
|
|
<Form.List name={["md_jobline_presets"]}>
|
|
{(fields, { add, remove, move }) => {
|
|
return (
|
|
<LayoutFormRow
|
|
grow
|
|
header={t("bodyshop.fields.md_jobline_presets")}
|
|
id="md_jobline_presets"
|
|
actions={[
|
|
buildSectionActionButton("add-jobline-preset", t("bodyshop.actions.add_jobline_preset"), () => {
|
|
add();
|
|
})
|
|
]}
|
|
>
|
|
<div>
|
|
{renderListOrEmpty(fields, t("bodyshop.actions.add_jobline_preset"), () =>
|
|
fields.map((field, index) => {
|
|
return (
|
|
<Form.Item noStyle key={field.key}>
|
|
<InlineValidatedFormRow
|
|
form={form}
|
|
errorNames={[
|
|
["md_jobline_presets", field.name, "label"],
|
|
["md_jobline_presets", field.name, "line_desc"]
|
|
]}
|
|
noDivider
|
|
title={
|
|
<div style={INLINE_TITLE_ROW_STYLE}>
|
|
<InlineTitleListIcon style={INLINE_TITLE_HANDLE_STYLE} />
|
|
<div style={INLINE_TITLE_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>{t("general.labels.label")}</div>
|
|
<Form.Item
|
|
noStyle
|
|
name={[field.name, "label"]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<Input
|
|
size="small"
|
|
placeholder={t("general.labels.label")}
|
|
style={{
|
|
...INLINE_TITLE_INPUT_STYLE,
|
|
width: "100%"
|
|
}}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
|
|
<div style={INLINE_TITLE_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>{t("joblines.fields.line_desc")}</div>
|
|
<Form.Item noStyle name={[field.name, "line_desc"]}>
|
|
<Input.TextArea
|
|
autoSize={{ minRows: 1, maxRows: 3 }}
|
|
placeholder={t("joblines.fields.line_desc")}
|
|
style={{
|
|
...INLINE_TITLE_INPUT_STYLE,
|
|
width: "100%",
|
|
paddingBlock: 5
|
|
}}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
</div>
|
|
}
|
|
wrapTitle
|
|
extra={
|
|
<Space align="center" size="small">
|
|
<Button
|
|
type="text"
|
|
danger
|
|
icon={<DeleteFilled />}
|
|
onClick={() => {
|
|
remove(field.name);
|
|
}}
|
|
/>
|
|
<FormListMoveArrows
|
|
move={move}
|
|
index={index}
|
|
total={fields.length}
|
|
orientation="horizontal"
|
|
/>
|
|
</Space>
|
|
}
|
|
>
|
|
<Form.Item
|
|
label={t("joblines.fields.mod_lbr_ty")}
|
|
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") }
|
|
]}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("joblines.fields.mod_lb_hrs")}
|
|
key={`${index}mod_lb_hrs`}
|
|
name={[field.name, "mod_lb_hrs"]}
|
|
>
|
|
<InputNumber precision={1} suffix="hrs" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("joblines.fields.part_type")}
|
|
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") }
|
|
]}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("joblines.fields.oem_partno")}
|
|
key={`${index}oem_partno`}
|
|
name={[field.name, "oem_partno"]}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("joblines.fields.part_qty")}
|
|
key={`${index}part_qty`}
|
|
name={[field.name, "part_qty"]}
|
|
>
|
|
<CurrencyInput precision={2} min={0} />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("joblines.fields.act_price")}
|
|
key={`${index}act_price`}
|
|
name={[field.name, "act_price"]}
|
|
>
|
|
<CurrencyInput precision={2} min={0} prefix="$" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("joblines.fields.prt_dsmk_p")}
|
|
key={`${index}prt_dsmk_p`}
|
|
name={[field.name, "prt_dsmk_p"]}
|
|
>
|
|
<InputNumber precision={0} min={-100} max={100} suffix="%" />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label={t("joblines.fields.ah_detail_line")}
|
|
key={`${index}ah_detail_line`}
|
|
name={[field.name, "ah_detail_line"]}
|
|
valuePropName="checked"
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
</InlineValidatedFormRow>
|
|
</Form.Item>
|
|
);
|
|
})
|
|
)}
|
|
</div>
|
|
</LayoutFormRow>
|
|
);
|
|
}}
|
|
</Form.List>
|
|
<Form.List name={["md_parts_order_comment"]}>
|
|
{(fields, { add, remove, move }) => {
|
|
return (
|
|
<LayoutFormRow
|
|
grow
|
|
header={t("bodyshop.fields.md_parts_order_comment")}
|
|
id="md_parts_order_comment"
|
|
actions={[
|
|
buildSectionActionButton(
|
|
"add-parts-order-comment",
|
|
t("bodyshop.actions.add_parts_order_comment"),
|
|
() => {
|
|
add();
|
|
}
|
|
)
|
|
]}
|
|
>
|
|
<div>
|
|
{renderListOrEmpty(fields, t("bodyshop.actions.add_parts_order_comment"), () =>
|
|
fields.map((field, index) => {
|
|
return (
|
|
<Form.Item noStyle key={field.key}>
|
|
<InlineValidatedFormRow
|
|
form={form}
|
|
errorNames={[["md_parts_order_comment", field.name, "label"]]}
|
|
noDivider
|
|
title={
|
|
<div style={INLINE_TITLE_ROW_STYLE}>
|
|
<InlineTitleListIcon style={INLINE_TITLE_HANDLE_STYLE} />
|
|
<div style={INLINE_TITLE_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>{t("general.labels.label")}</div>
|
|
<Form.Item
|
|
noStyle
|
|
name={[field.name, "label"]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<Input
|
|
size="small"
|
|
placeholder={t("general.labels.label")}
|
|
style={{
|
|
...INLINE_TITLE_INPUT_STYLE,
|
|
width: "100%"
|
|
}}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
</div>
|
|
}
|
|
wrapTitle
|
|
extra={
|
|
<Space align="center" size="small">
|
|
<Button
|
|
type="text"
|
|
danger
|
|
icon={<DeleteFilled />}
|
|
onClick={() => {
|
|
remove(field.name);
|
|
}}
|
|
/>
|
|
<FormListMoveArrows
|
|
move={move}
|
|
index={index}
|
|
total={fields.length}
|
|
orientation="horizontal"
|
|
/>
|
|
</Space>
|
|
}
|
|
>
|
|
<Form.Item
|
|
label={t("parts_orders.fields.comments")}
|
|
name={[field.name, "comment"]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
style={{ marginBottom: 0 }}
|
|
>
|
|
<Input.TextArea
|
|
autoSize={{ minRows: 1, maxRows: 3 }}
|
|
placeholder={t("parts_orders.fields.comments")}
|
|
/>
|
|
</Form.Item>
|
|
</InlineValidatedFormRow>
|
|
</Form.Item>
|
|
);
|
|
})
|
|
)}
|
|
</div>
|
|
</LayoutFormRow>
|
|
);
|
|
}}
|
|
</Form.List>
|
|
<Form.List name={["md_to_emails"]}>
|
|
{(fields, { add, remove, move }) => {
|
|
return (
|
|
<LayoutFormRow
|
|
grow
|
|
header={t("bodyshop.labels.md_to_emails")}
|
|
id="md_to_emails"
|
|
actions={[
|
|
buildSectionActionButton("add-to-email-preset", t("bodyshop.actions.add_to_email_preset"), () => {
|
|
add();
|
|
})
|
|
]}
|
|
>
|
|
<div>
|
|
{renderListOrEmpty(fields, t("bodyshop.actions.add_to_email_preset"), () =>
|
|
fields.map((field, index) => {
|
|
return (
|
|
<Form.Item noStyle key={field.key}>
|
|
<InlineValidatedFormRow
|
|
form={form}
|
|
errorNames={[["md_to_emails", field.name, "label"]]}
|
|
noDivider
|
|
title={
|
|
<div style={INLINE_TITLE_ROW_STYLE}>
|
|
<InlineTitleListIcon style={INLINE_TITLE_HANDLE_STYLE} />
|
|
<div style={INLINE_TITLE_GROUP_STYLE}>
|
|
<div style={INLINE_TITLE_LABEL_STYLE}>{t("general.labels.label")}</div>
|
|
<Form.Item
|
|
noStyle
|
|
name={[field.name, "label"]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
>
|
|
<Input
|
|
size="small"
|
|
placeholder={t("general.labels.label")}
|
|
style={{
|
|
...INLINE_TITLE_INPUT_STYLE,
|
|
width: "100%"
|
|
}}
|
|
/>
|
|
</Form.Item>
|
|
</div>
|
|
</div>
|
|
}
|
|
wrapTitle
|
|
extra={
|
|
<Space align="center" size="small">
|
|
<Button
|
|
type="text"
|
|
danger
|
|
icon={<DeleteFilled />}
|
|
onClick={() => {
|
|
remove(field.name);
|
|
}}
|
|
/>
|
|
<FormListMoveArrows
|
|
move={move}
|
|
index={index}
|
|
total={fields.length}
|
|
orientation="horizontal"
|
|
/>
|
|
</Space>
|
|
}
|
|
>
|
|
<Form.Item
|
|
label={t("bodyshop.labels.md_to_emails_emails")}
|
|
name={[field.name, "emails"]}
|
|
rules={[
|
|
{
|
|
required: true
|
|
//message: t("general.validation.required"),
|
|
}
|
|
]}
|
|
style={{ marginBottom: 0 }}
|
|
>
|
|
<FormItemEmail email={form.getFieldValue([field.name, "emails"])} />
|
|
</Form.Item>
|
|
</InlineValidatedFormRow>
|
|
</Form.Item>
|
|
);
|
|
})
|
|
)}
|
|
</div>
|
|
</LayoutFormRow>
|
|
);
|
|
}}
|
|
</Form.List>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function getDuplicateIndexSetByNormalizedName(list, key) {
|
|
const indexes = new Set();
|
|
const firstIndexByValue = new Map();
|
|
|
|
(Array.isArray(list) ? list : []).forEach((item, index) => {
|
|
const normalizedValue = (item?.[key] ?? "").toString().trim().toLowerCase();
|
|
if (!normalizedValue) return;
|
|
|
|
if (firstIndexByValue.has(normalizedValue)) {
|
|
indexes.add(firstIndexByValue.get(normalizedValue));
|
|
indexes.add(index);
|
|
return;
|
|
}
|
|
|
|
firstIndexByValue.set(normalizedValue, index);
|
|
});
|
|
|
|
return indexes;
|
|
}
|