Merge branch 'master-AIO' into feature/IO-3356-pbs-ro-posting

This commit is contained in:
Patrick Fic
2025-09-11 14:40:08 -07:00
758 changed files with 12971 additions and 8456 deletions

View File

@@ -9,7 +9,7 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({});
const mapDispatchToProps = () => ({});
function ShopInfoConsentComponent({ bodyshop }) {
const { t } = useTranslation();

View File

@@ -23,13 +23,24 @@ export default function ShopInfoContainer() {
});
const notification = useNotification();
const combinedFeatureConfig = {
...FEATURE_CONFIGS.general,
...FEATURE_CONFIGS.responsibilitycenters
};
const combineFeatureConfigs = (...configs) =>
(configs || [])
.filter(Boolean)
.flatMap((cfg) => Object.entries(cfg))
.reduce((acc, [featureName, fieldPaths]) => {
if (!Array.isArray(fieldPaths)) return acc;
acc[featureName] = [...(acc[featureName] ?? []), ...fieldPaths];
return acc;
}, {});
const combinedFeatureConfig = combineFeatureConfigs(FEATURE_CONFIGS.general, FEATURE_CONFIGS.responsibilitycenters);
// Use form data preservation for all shop-info features
const { createSubmissionHandler } = useFormDataPreservation(form, data?.bodyshops[0], combinedFeatureConfig);
const { createSubmissionHandler, preserveHiddenFormData } = useFormDataPreservation(
form,
data?.bodyshops[0],
combinedFeatureConfig
);
const handleFinish = createSubmissionHandler((values) => {
setSaveLoading(true);
@@ -51,8 +62,11 @@ export default function ShopInfoContainer() {
});
useEffect(() => {
if (data) form.resetFields();
}, [form, data]);
if (!data) return;
form.resetFields();
// After reset, re-apply hidden field preservation so values aren't wiped
preserveHiddenFormData();
}, [data, form, preserveHiddenFormData]);
if (error) return <AlertComponent message={error.message} type="error" />;
if (loading) return <LoadingSpinner />;
@@ -64,7 +78,7 @@ export default function ShopInfoContainer() {
onFinish={handleFinish}
initialValues={
data
? data.bodyshops[0].accountingconfig.ClosingPeriod
? data?.bodyshops?.[0]?.accountingconfig?.ClosingPeriod
? {
...data.bodyshops[0],
accountingconfig: {

View File

@@ -14,7 +14,6 @@ import PhoneFormItem, { PhoneItemFormatterValidation } from "../form-items-forma
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
// eslint-disable-next-line no-undef
const timeZonesList = Intl.supportedValuesOf("timeZone");
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
@@ -32,7 +31,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
} = useSplitTreatments({
attributes: {},
names: ["ClosingPeriod", "ADPPayroll"],
splitKey: bodyshop && bodyshop.imexshopid
splitKey: bodyshop?.imexshopid
});
return (
@@ -543,7 +542,12 @@ export function ShopInfoGeneral({ form, bodyshop }) {
>
<InputNumber min={0.1} precision={1} />
</Form.Item>,
<Form.Item key="use_fippa" label={t("bodyshop.fields.use_fippa")} name={["use_fippa"]} valuePropName="checked">
<Form.Item
key="use_fippa"
label={t("bodyshop.fields.use_fippa")}
name={["use_fippa"]}
valuePropName="checked"
>
<Switch />
</Form.Item>,
<Form.Item
@@ -924,7 +928,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
</LayoutFormRow>
<LayoutFormRow
grow
header=<span id="insurancecos-header">{t("bodyshop.labels.insurancecos")}</span>
header={<span id="insurancecos-header">{t("bodyshop.labels.insurancecos")}</span>}
id="insurancecos"
>
<Form.List name={["md_ins_cos"]}>
@@ -1179,7 +1183,17 @@ export function ShopInfoGeneral({ form, bodyshop }) {
{fields.map((field, index) => (
<Form.Item key={field.key}>
<LayoutFormRow noDivider>
<Form.Item label={t("general.labels.label")} key={`${index}label`} name={[field.name, "label"]}>
<Form.Item
label={t("general.labels.label")}
key={`${index}label`}
name={[field.name, "label"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Input />
</Form.Item>
<Form.Item
@@ -1290,7 +1304,17 @@ export function ShopInfoGeneral({ form, bodyshop }) {
{fields.map((field, index) => (
<Form.Item key={field.key}>
<LayoutFormRow noDivider>
<Form.Item label={t("general.labels.label")} key={`${index}label`} name={[field.name, "label"]}>
<Form.Item
label={t("general.labels.label")}
key={`${index}label`}
name={[field.name, "label"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Input />
</Form.Item>
<Form.Item
@@ -1479,15 +1503,31 @@ export function ShopInfoGeneral({ form, bodyshop }) {
{fields.map((field, index) => (
<Form.Item key={field.key}>
<LayoutFormRow noDivider>
<Form.Item label={t("general.labels.label")} key={`${index}label`} name={[field.name, "label"]}>
<Form.Item
label={t("general.labels.label")}
key={`${index}label`}
name={[field.name, "label"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.labels.md_to_emails_emails")}
key={`${index}emails`}
name={[field.name, "emails"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Select mode="tags" tokenSeparators={[",", ";"]} />
<FormItemEmail email={form.getFieldValue([field.name, "emails"])} />
</Form.Item>
<Space>

View File

@@ -1,6 +1,5 @@
import { DeleteFilled } from "@ant-design/icons";
import { Button, Form, Input, InputNumber, Select, Space, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import styled from "styled-components";
import { TemplateList } from "../../utils/TemplateConstants";
@@ -53,7 +52,7 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
]}
>
<Select>
{Object.keys(ConfigFormTypes).map((i, idx) => (
{Object.keys(ConfigFormTypes).map((i) => (
<Select.Option key={i} value={i}>
{i}
</Select.Option>
@@ -207,7 +206,7 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
]}
>
<Select>
{Object.keys(ConfigFormTypes).map((i, idx) => (
{Object.keys(ConfigFormTypes).map((i) => (
<Select.Option key={i} value={i}>
{i}
</Select.Option>

View File

@@ -5,7 +5,7 @@ import CurrencyInput from "../form-items-formatted/currency-form-item.component"
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
export default function ShopInfoLaborRates({ form }) {
export default function ShopInfoLaborRates() {
const { t } = useTranslation();
return (

View File

@@ -1,5 +1,4 @@
import { Form, Input } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { connect } from "react-redux";
@@ -10,12 +9,12 @@ import { useSplitTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({
const mapDispatchToProps = () => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoOrderStatusComponent);
export function ShopInfoOrderStatusComponent({ bodyshop, form }) {
export function ShopInfoOrderStatusComponent({ bodyshop }) {
const { t } = useTranslation();
const {

View File

@@ -1,5 +1,4 @@
import { Collapse, Divider, Form, Input, InputNumber, Space, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -11,7 +10,7 @@ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({
const mapDispatchToProps = () => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoResponsibilityCenters);

View File

@@ -1,5 +1,4 @@
import { Form, Input, InputNumber, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import InstanceRenderManager from "../../utils/instanceRenderMgr";

View File

@@ -1,6 +1,6 @@
import { DeleteFilled } from "@ant-design/icons";
import { Button, Form, Select, Space } from "antd";
import React, { useState } from "react";
import { useState } from "react";
import { ChromePicker } from "react-color";
import { useTranslation } from "react-i18next";
import styled from "styled-components";
@@ -15,7 +15,7 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({
const mapDispatchToProps = () => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoROStatusComponent);
@@ -40,9 +40,7 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
const [options, setOptions] = useState(form.getFieldValue(["md_ro_statuses", "statuses"]) || []);
const [productionStatus, setProductionStatus] = useState(
(
form.getFieldValue(["md_ro_statuses", "production_statuses"]) || []
).concat(
(form.getFieldValue(["md_ro_statuses", "production_statuses"]) || []).concat(
form.getFieldValue(["md_ro_statuses", "additional_board_statuses"]) || []
) || []
);
@@ -52,9 +50,7 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
setProductionStatus(
form
.getFieldValue(["md_ro_statuses", "production_statuses"])
.concat(
form.getFieldValue(["md_ro_statuses", "additional_board_statuses"])
)
.concat(form.getFieldValue(["md_ro_statuses", "additional_board_statuses"]))
);
};
@@ -336,10 +332,10 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
{Production_List_Status_Colors.treatment === "on" && (
<LayoutFormRow grow header={t("bodyshop.fields.statuses.production_colors")} id="production_colors">
<Form.List name={["md_ro_statuses", "production_colors"]}>
{(fields, { add, remove, move }) => {
{(fields, { add, remove }) => {
return (
<div>
<Space size='large' wrap>
<Space size="large" wrap>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<Space direction="vertical">
@@ -408,7 +404,7 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
);
}
export const ColorPicker = ({ value, onChange, style, ...restProps }) => {
export const ColorPicker = ({ value, onChange, ...restProps }) => {
const handleChange = (color) => {
if (onChange) onChange(color.rgb);
};

View File

@@ -1,12 +1,11 @@
import { DeleteFilled } from "@ant-design/icons";
import { Button, Form, Input, Select, Space } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { TemplateList } from "../../utils/TemplateConstants";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
export default function ShopInfoSpeedPrint({ bodyshop, form }) {
export default function ShopInfoSpeedPrint() {
const { t } = useTranslation();
const TemplateListGenerated = TemplateList("job");
return (

View File

@@ -1,6 +1,5 @@
import { DeleteFilled } from "@ant-design/icons";
import { Button, Checkbox, Col, Form, Input, InputNumber, Row, Select, Space, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
@@ -12,12 +11,12 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({
const mapDispatchToProps = () => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoTaskPresets);
export function ShopInfoTaskPresets({ bodyshop, form }) {
export function ShopInfoTaskPresets({ bodyshop }) {
const { t } = useTranslation();
return (

View File

@@ -1,4 +1,4 @@
import { useCallback, useEffect } from "react";
import { useCallback, useEffect, useMemo } from "react";
import { HasFeatureAccess } from "./../feature-wrapper/feature-wrapper.component";
/**
@@ -8,73 +8,57 @@ import { HasFeatureAccess } from "./../feature-wrapper/feature-wrapper.component
* @param {Object} featureConfig - Configuration object defining which features and their associated fields to preserve
*/
export const useFormDataPreservation = (form, bodyshop, featureConfig) => {
const getNestedValue = (obj, path) => {
return path.reduce((current, key) => current?.[key], obj);
};
// Safe nested getters/setters using path arrays
const getNestedValue = (obj, path) => path?.reduce((acc, key) => acc?.[key], obj);
const setNestedValue = (obj, path, value) => {
const lastKey = path[path.length - 1];
const parentPath = path.slice(0, -1);
const parent = parentPath.reduce((current, key) => {
if (!current[key]) current[key] = {};
return current[key];
const parent = path.slice(0, -1).reduce((curr, key) => {
if (!curr[key] || typeof curr[key] !== "object") curr[key] = {};
return curr[key];
}, obj);
parent[lastKey] = value;
};
const preserveHiddenFormData = useCallback(() => {
const preservationData = {};
let hasDataToPreserve = false;
// Paths for features that are NOT accessible
const disabledPaths = useMemo(() => {
const result = [];
if (!featureConfig) return result;
Object.entries(featureConfig).forEach(([featureName, fieldPaths]) => {
const hasAccess = HasFeatureAccess({ featureName, bodyshop });
if (hasAccess || !Array.isArray(fieldPaths)) return;
fieldPaths.forEach((p) => Array.isArray(p) && p.length && result.push(p));
});
return result;
}, [featureConfig, bodyshop]);
if (!hasAccess) {
fieldPaths.forEach((fieldPath) => {
const currentValues = form.getFieldsValue();
let value = getNestedValue(currentValues, fieldPath);
const preserveHiddenFormData = useCallback(() => {
const currentValues = form.getFieldsValue();
const preservationData = {};
let hasAny = false;
if (value === undefined || value === null) {
value = getNestedValue(bodyshop, fieldPath);
}
if (value !== undefined && value !== null) {
setNestedValue(preservationData, fieldPath, value);
hasDataToPreserve = true;
}
});
disabledPaths.forEach((path) => {
let value = getNestedValue(currentValues, path);
if (value == null) value = getNestedValue(bodyshop, path);
if (value != null) {
setNestedValue(preservationData, path, value);
hasAny = true;
}
});
if (hasDataToPreserve) {
form.setFieldsValue(preservationData);
}
}, [form, featureConfig, bodyshop]);
if (hasAny) form.setFieldsValue(preservationData);
}, [form, bodyshop, disabledPaths]);
const getCompleteFormValues = () => {
const currentFormValues = form.getFieldsValue();
const completeValues = { ...currentFormValues };
const currentValues = form.getFieldsValue();
const complete = { ...currentValues };
Object.entries(featureConfig).forEach(([featureName, fieldPaths]) => {
const hasAccess = HasFeatureAccess({ featureName, bodyshop });
if (!hasAccess) {
fieldPaths.forEach((fieldPath) => {
let value = getNestedValue(currentFormValues, fieldPath);
if (value === undefined || value === null) {
value = getNestedValue(bodyshop, fieldPath);
}
if (value !== undefined && value !== null) {
setNestedValue(completeValues, fieldPath, value);
}
});
}
disabledPaths.forEach((path) => {
let value = getNestedValue(currentValues, path);
if (value == null) value = getNestedValue(bodyshop, path);
if (value != null) setNestedValue(complete, path, value);
});
return completeValues;
return complete;
};
const createSubmissionHandler = (originalHandler) => {
@@ -103,8 +87,8 @@ export const FEATURE_CONFIGS = {
["md_responsibility_centers", "profits"],
["md_responsibility_centers", "defaults"],
["md_responsibility_centers", "dms_defaults"],
["md_responsibility_centers", "taxes", "itemexemptcode"],
["md_responsibility_centers", "taxes", "invoiceexemptcode"],
["md_responsibility_centers", "taxes"],
["md_responsibility_centers", "cieca_pfl"],
["md_responsibility_centers", "ar"],
["md_responsibility_centers", "refund"],
["md_responsibility_centers", "sales_tax_codes"],