IO-3624 Polish remaining shop config section cards

This commit is contained in:
Dave
2026-03-24 11:51:55 -04:00
parent d23a182650
commit c33a3118bc
9 changed files with 302 additions and 287 deletions

View File

@@ -22,6 +22,66 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
const deliverChecklistItems = Form.useWatch(["deliverchecklist", "form"], form) || [];
return (
<div>
<SelectorDiv>
<LayoutFormRow header={t("bodyshop.labels.intake_delivery")} id="intake-delivery">
<Form.Item
name={["intakechecklist", "templates"]}
label={t("bodyshop.fields.intake.templates")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select
mode="multiple"
options={Object.keys(TemplateListGenerated).map((i) => ({
value: TemplateListGenerated[i].key,
label: TemplateListGenerated[i].title
}))}
/>
</Form.Item>
<Form.Item
name={["intakechecklist", "next_contact_hours"]}
label={t("bodyshop.fields.intake.next_contact_hours")}
>
<InputNumber min={0} precision={0} />
</Form.Item>
<Form.Item
name={["deliverchecklist", "templates"]}
label={t("bodyshop.fields.deliver.templates")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select
mode="multiple"
options={Object.keys(TemplateListGenerated).map((i) => ({
value: TemplateListGenerated[i].key,
label: TemplateListGenerated[i].title
}))}
/>
</Form.Item>
<Form.Item
name={["deliverchecklist", "actual_delivery"]}
label={t("bodyshop.fields.deliver.require_actual_delivery_date")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Switch />
</Form.Item>
</LayoutFormRow>
</SelectorDiv>
<LayoutFormRow header={t("bodyshop.labels.intakechecklist")} id="intakechecklist">
<Form.List name={["intakechecklist", "form"]}>
{(fields, { add, remove, move }) => {
@@ -163,34 +223,6 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
}}
</Form.List>
</LayoutFormRow>
<SelectorDiv>
<Form.Item
name={["intakechecklist", "templates"]}
label={t("bodyshop.fields.intake.templates")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select
mode="multiple"
options={Object.keys(TemplateListGenerated).map((i) => ({
value: TemplateListGenerated[i].key,
label: TemplateListGenerated[i].title
}))}
/>
</Form.Item>
<Form.Item
name={["intakechecklist", "next_contact_hours"]}
label={t("bodyshop.fields.intake.next_contact_hours")}
>
<InputNumber min={0} precision={0} />
</Form.Item>
</SelectorDiv>
<LayoutFormRow header={t("bodyshop.labels.deliverchecklist")} id="deliverchecklist">
<Form.List name={["deliverchecklist", "form"]}>
{(fields, { add, remove, move }) => {
@@ -335,39 +367,6 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
}}
</Form.List>
</LayoutFormRow>
<SelectorDiv>
<Form.Item
name={["deliverchecklist", "templates"]}
label={t("bodyshop.fields.deliver.templates")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select
mode="multiple"
options={Object.keys(TemplateListGenerated).map((i) => ({
value: TemplateListGenerated[i].key,
label: TemplateListGenerated[i].title
}))}
/>
</Form.Item>
<Form.Item
name={["deliverchecklist", "actual_delivery"]}
label={t("bodyshop.fields.deliver.require_actual_delivery_date")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Switch />
</Form.Item>
</SelectorDiv>
</div>
);
}

View File

@@ -1,6 +1,7 @@
import { Form, Typography } from "antd";
import { useTranslation } from "react-i18next";
import EmployeeSearchSelectComponent from "../employee-search-select/employee-search-select.component.jsx";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const { Text, Paragraph } = Typography;
@@ -11,43 +12,45 @@ export default function ShopInfoNotificationsAutoadd({ bodyshop }) {
const employeeOptions = bodyshop?.employees?.filter((e) => e.active && e.user_email && e.id) || [];
return (
<div>
<Paragraph>{t("bodyshop.fields.notifications.description")}</Paragraph>
<Text type="secondary">{t("bodyshop.labels.notifications.followers")}</Text>
{employeeOptions.length > 0 ? (
<Form.Item
normalize={(value) => (value || []).filter((id) => typeof id === "string" && id.trim() !== "")}
name="notification_followers"
rules={[
{
type: "array",
message: t("general.validation.array")
},
{
validator: async (_, value) => {
if (!value || value.length === 0) {
return Promise.resolve(); // Allow empty array
<LayoutFormRow header={t("bodyshop.labels.notification_options")}>
<div>
<Paragraph>{t("bodyshop.fields.notifications.description")}</Paragraph>
<Text type="secondary">{t("bodyshop.labels.notifications.followers")}</Text>
{employeeOptions.length > 0 ? (
<Form.Item
normalize={(value) => (value || []).filter((id) => typeof id === "string" && id.trim() !== "")}
name="notification_followers"
rules={[
{
type: "array",
message: t("general.validation.array")
},
{
validator: async (_, value) => {
if (!value || value.length === 0) {
return Promise.resolve(); // Allow empty array
}
const hasInvalid = value.some((id) => id == null || typeof id !== "string" || id.trim() === "");
if (hasInvalid) {
return Promise.reject(new Error(t("bodyshop.fields.notifications.invalid_followers")));
}
return Promise.resolve();
}
const hasInvalid = value.some((id) => id == null || typeof id !== "string" || id.trim() === "");
if (hasInvalid) {
return Promise.reject(new Error(t("bodyshop.fields.notifications.invalid_followers")));
}
return Promise.resolve();
}
}
]}
>
<EmployeeSearchSelectComponent
style={{ minWidth: "100%" }}
mode="multiple"
options={employeeOptions}
placeholder={t("bodyshop.fields.notifications.placeholder")}
showEmail={true}
/>
</Form.Item>
) : (
<Text type="secondary">{t("bodyshop.fields.no_employees_available")}</Text>
)}
</div>
]}
>
<EmployeeSearchSelectComponent
style={{ minWidth: "100%" }}
mode="multiple"
options={employeeOptions}
placeholder={t("bodyshop.fields.notifications.placeholder")}
showEmail={true}
/>
</Form.Item>
) : (
<Text type="secondary">{t("bodyshop.fields.no_employees_available")}</Text>
)}
</div>
</LayoutFormRow>
);
}

View File

@@ -27,7 +27,7 @@ export function ShopInfoRbacComponent({ bodyshop }) {
});
return (
<RbacWrapper action="shop:rbac">
<LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.rbac_options")}>
{[
...(HasFeatureAccess({ featureName: "export", bodyshop })
? [

View File

@@ -21,7 +21,7 @@ export default function ShopInfoRoGuard({ form }) {
{() => {
const disabled = !form.getFieldValue(["md_ro_guard", "enabled"]);
return (
<LayoutFormRow noDivider>
<LayoutFormRow header={t("bodyshop.labels.md_ro_guard_options")}>
<Form.Item
label={t("bodyshop.fields.md_ro_guard.totalgppercent_minimum")}
name={["md_ro_guard", "totalgppercent_minimum"]}

View File

@@ -45,98 +45,102 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
return (
<SelectorDiv id="jobstatus">
<Form.Item
name={["md_ro_statuses", "statuses"]}
label={t("bodyshop.labels.alljobstatuses")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select mode="tags" />
</Form.Item>
<Form.Item
name={["md_ro_statuses", "active_statuses"]}
label={t("bodyshop.fields.statuses.active_statuses")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select mode="multiple" options={statusOptions.map((item) => ({ value: item, label: item }))} />
</Form.Item>
<Form.Item
name={["md_ro_statuses", "pre_production_statuses"]}
label={t("bodyshop.fields.statuses.pre_production_statuses")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select mode="multiple" options={statusOptions.map((item) => ({ value: item, label: item }))} />
</Form.Item>
<Form.Item
name={["md_ro_statuses", "production_statuses"]}
label={t("bodyshop.fields.statuses.production_statuses")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select mode="multiple" options={statusOptions.map((item) => ({ value: item, label: item }))} />
</Form.Item>
<Form.Item
name={["md_ro_statuses", "post_production_statuses"]}
label={t("bodyshop.fields.statuses.post_production_statuses")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select mode="multiple" options={statusOptions.map((item) => ({ value: item, label: item }))} />
</Form.Item>
<Form.Item
name={["md_ro_statuses", "ready_statuses"]}
label={t("bodyshop.fields.statuses.ready_statuses")}
rules={[
{
//required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select mode="multiple" options={statusOptions.map((item) => ({ value: item, label: item }))} />
</Form.Item>
<Form.Item
name={["md_ro_statuses", "additional_board_statuses"]}
label={t("bodyshop.fields.statuses.additional_board_statuses")}
rules={[
{
//required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select mode="multiple" options={statusOptions.map((item) => ({ value: item, label: item }))} />
</Form.Item>
<LayoutFormRow noDivider>
<LayoutFormRow grow header={t("bodyshop.labels.job_status_options")}>
<div>
<Form.Item
name={["md_ro_statuses", "statuses"]}
label={t("bodyshop.labels.alljobstatuses")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select mode="tags" />
</Form.Item>
<Form.Item
name={["md_ro_statuses", "active_statuses"]}
label={t("bodyshop.fields.statuses.active_statuses")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select mode="multiple" options={statusOptions.map((item) => ({ value: item, label: item }))} />
</Form.Item>
<Form.Item
name={["md_ro_statuses", "pre_production_statuses"]}
label={t("bodyshop.fields.statuses.pre_production_statuses")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select mode="multiple" options={statusOptions.map((item) => ({ value: item, label: item }))} />
</Form.Item>
<Form.Item
name={["md_ro_statuses", "production_statuses"]}
label={t("bodyshop.fields.statuses.production_statuses")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select mode="multiple" options={statusOptions.map((item) => ({ value: item, label: item }))} />
</Form.Item>
<Form.Item
name={["md_ro_statuses", "post_production_statuses"]}
label={t("bodyshop.fields.statuses.post_production_statuses")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select mode="multiple" options={statusOptions.map((item) => ({ value: item, label: item }))} />
</Form.Item>
<Form.Item
name={["md_ro_statuses", "ready_statuses"]}
label={t("bodyshop.fields.statuses.ready_statuses")}
rules={[
{
//required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select mode="multiple" options={statusOptions.map((item) => ({ value: item, label: item }))} />
</Form.Item>
<Form.Item
name={["md_ro_statuses", "additional_board_statuses"]}
label={t("bodyshop.fields.statuses.additional_board_statuses")}
rules={[
{
//required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select mode="multiple" options={statusOptions.map((item) => ({ value: item, label: item }))} />
</Form.Item>
</div>
</LayoutFormRow>
<LayoutFormRow grow header={t("general.actions.defaults")}>
<Form.Item
label={t("bodyshop.fields.statuses.default_scheduled")}
rules={[

View File

@@ -18,100 +18,102 @@ export default function ShopInfoSpeedPrint() {
});
return (
<Form.List name={["speedprint"]}>
{(fields, { add, remove, move }) => {
return (
<div>
{fields.map((field, index) => {
const speedPrintItem = speedPrintItems[field.name] || {};
<LayoutFormRow header={t("bodyshop.labels.speedprint_configurations")}>
<Form.List name={["speedprint"]}>
{(fields, { add, remove, move }) => {
return (
<div>
{fields.map((field, index) => {
const speedPrintItem = speedPrintItems[field.name] || {};
return (
<Form.Item key={field.key} style={{ padding: 0, margin: 2 }}>
<LayoutFormRow
grow
title={getFormListItemTitle(
t("bodyshop.fields.speedprint.label"),
index,
speedPrintItem.label,
speedPrintItem.id
)}
extra={
<Space align="center" size="small">
<Button
type="text"
icon={<DeleteFilled />}
onClick={() => {
remove(field.name);
}}
return (
<Form.Item key={field.key} style={{ padding: 0, margin: 2 }}>
<LayoutFormRow
grow
title={getFormListItemTitle(
t("bodyshop.fields.speedprint.label"),
index,
speedPrintItem.label,
speedPrintItem.id
)}
extra={
<Space align="center" size="small">
<Button
type="text"
icon={<DeleteFilled />}
onClick={() => {
remove(field.name);
}}
/>
<FormListMoveArrows move={move} index={index} total={fields.length} orientation="horizontal" />
</Space>
}
>
<Form.Item
label={t("bodyshop.fields.speedprint.id")}
key={`${index}id`}
name={[field.name, "id"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.speedprint.label")}
key={`${index}label`}
name={[field.name, "label"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Input />
</Form.Item>
<Form.Item
name={[field.name, "templates"]}
label={t("bodyshop.fields.speedprint.templates")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select
mode="multiple"
options={Object.keys(TemplateListGenerated).map((key) => ({
value: TemplateListGenerated[key].key,
label: TemplateListGenerated[key].title
}))}
/>
<FormListMoveArrows move={move} index={index} total={fields.length} orientation="horizontal" />
</Space>
}
>
<Form.Item
label={t("bodyshop.fields.speedprint.id")}
key={`${index}id`}
name={[field.name, "id"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.speedprint.label")}
key={`${index}label`}
name={[field.name, "label"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Input />
</Form.Item>
<Form.Item
name={[field.name, "templates"]}
label={t("bodyshop.fields.speedprint.templates")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array"
}
]}
>
<Select
mode="multiple"
options={Object.keys(TemplateListGenerated).map((key) => ({
value: TemplateListGenerated[key].key,
label: TemplateListGenerated[key].title
}))}
/>
</Form.Item>
</LayoutFormRow>
</Form.Item>
);
})}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("bodyshop.actions.addspeedprint")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</Form.Item>
</LayoutFormRow>
</Form.Item>
);
})}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("bodyshop.actions.addspeedprint")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</LayoutFormRow>
);
}

View File

@@ -61,7 +61,7 @@ export function ShopInfoTaskPresets({ bodyshop }) {
return (
<>
<LayoutFormRow noDivider>
<LayoutFormRow header={t("bodyshop.labels.task_preset_options")}>
<Form.Item
label={t("bodyshop.fields.md_tasks_presets.enable_tasks")}
valuePropName="checked"

View File

@@ -20,7 +20,7 @@ export function ShopInfoIntellipay({ bodyshop, form }) {
return (
<>
<Form.Item dependencies={[["intellipay_config", "enable_cash_discount"]]}>
<Form.Item dependencies={[["intellipay_config", "enable_cash_discount"]]} style={{ marginBottom: 20 }}>
{() => {
const { intellipay_config } = form.getFieldsValue();
@@ -29,7 +29,7 @@ export function ShopInfoIntellipay({ bodyshop, form }) {
}}
</Form.Item>
<LayoutFormRow noDivider>
<LayoutFormRow header={t("bodyshop.labels.imexpay")}>
<Form.Item
label={t("bodyshop.fields.intellipay_config.enable_cash_discount")}
valuePropName="checked"

View File

@@ -741,17 +741,22 @@
"filehandlers": "Adjusters",
"imexpay": "ImEX Pay",
"insurancecos": "Insurance Companies",
"intake_delivery": "Intake / Delivery Options",
"intakechecklist": "Intake Checklist",
"intellipay_cash_discount": "Please ensure that cash discounting has been enabled on your merchant account. Reach out to IntelliPay Support if you need assistance. ",
"job_status_options": "Job Status Options",
"jobstatuses": "Job Statuses",
"laborrates": "Labor Rates",
"licensing": "Licensing",
"md_parts_scan": "Parts Scan Rules",
"md_ro_guard": "RO Guard",
"md_ro_guard_options": "RO Guard Options",
"md_tasks_presets": "Tasks Presets",
"task_preset_options": "Task Preset Options",
"md_to_emails": "Preset To Emails",
"md_to_emails_emails": "Emails",
"messagingpresets": "Messaging Presets",
"notification_options": "Notification Options",
"notemplatesavailable": "No templates available to add.",
"notespresets": "Notes Presets",
"notifications": {
@@ -767,6 +772,7 @@
"qbo_departmentid": "QBO Department ID",
"qbo_usa": "QBO USA Compatibility",
"rbac": "Role Based Access Control",
"rbac_options": "Role Based Access Control Options",
"responsibilitycenters": {
"costs": "Cost Centers",
"profits": "Profit Centers",
@@ -786,6 +792,7 @@
"shopinfo": "Shop Information",
"shoprates": "Shop Rates",
"speedprint": "Speed Print Configuration",
"speedprint_configurations": "Speed Print Configurations",
"ssbuckets": "Job Size Definitions",
"systemsettings": "System Settings",
"task-presets": "Task Presets",