Merge pull request 'Fix #5: Support custom fields in Quick Add screen' (#17) from feature/5-quick-add-custom-fields into main

Reviewed-on: Pozomind/pozo-retail-app#17
This commit is contained in:
karthikalakshmi 2026-01-28 23:29:21 -05:00
commit ee474db911
2 changed files with 220 additions and 88 deletions

View File

@ -6,6 +6,7 @@ import {
PlusCircleOutlined,
InfoCircleOutlined,
} from '@ant-design/icons';
import { IoSettingsOutline } from "react-icons/io5";
import Buttons from '../../../../Components/Forms/Buttons.jsx';
import { InputField } from '../../../../Components/Forms/InputField.jsx';
import { Messages } from '../../../../Components/Notifications/Messages';
@ -31,7 +32,9 @@ import {
supplierSelector,
postProductData,
postTax,
getHsnData
getHsnData,
getFieldSetupData,
postFieldSetup
} from '../../../../Features/ProductPage/ProductPage.js';
import {
ChangeNewQrProduct,
@ -78,8 +81,12 @@ const ProductForm = ({
const productFieldPreferences = appPreferences?.find(
(preference) => preference?.PreferredCatName?.toLowerCase() === "product form fields"
)?.PreferenceCatDetails;
// const CustomQuickAddField = appPreferences?.find(
// (preference) => preference?.PreferredCatName?.toLowerCase() === "quick add custom fields"
// )?.PreferenceCatDetails;
const categoryId = appPreferences?.find(
p => p?.PreferredCatName?.toLowerCase() === "quick add custom fields"
)?.PreferredCatId;
const UomTypePreference = appPreferences?.find((preference) => preference?.PreferredCatName === 'Product Uom')?.PreferenceCatDetails
const UomPreference = UomData?.filter((item) => UomTypePreference?.some?.((e) => e?.PreferredSubCatName?.toLowerCase() == item?.ConfigName?.toLowerCase() && e?.PreferredStatus == "Y"))
@ -87,6 +94,7 @@ const ProductForm = ({
const filteredFields = productFieldPreferences?.filter(
(field) => field?.PreferredStatus === 'Y'
);
const cessField = filteredFields?.find((field) => field?.PreferredSubCatName === 'Cess');
const hsnField = filteredFields?.find((field) => field?.PreferredSubCatName === 'HSN');
const taxField = filteredFields?.find((field) => field?.PreferredSubCatName === 'Tax');
@ -95,15 +103,21 @@ const ProductForm = ({
const [messageType, setMessageType] = useState(null);
const [messageData, setMessageData] = useState(null);
const [SelectedUom, setSelectedUom] = useState(null);
const [SelectedTaxId, setSelectedTaxId] = useState(null);
const [SelectedTaxNameId, setSelectedTaxNameId] = useState(null);
const [OpenTaxModel, setOpenTaxModel] = useState(false);
const [OpenAdd, setOpenAdd] = useState(false);
// const [OpenAdd, setOpenAdd] = useState(false);
const [imageBrandUrl, setBrandImageUrl] = useState('');
const [selectedHsn, setSelectedHsn] = useState("");
const [HsnDetails, setHsnDetails] = useState([]);
const [selectedAdditionalColumn, setSelectedAdditionalColumn] = useState([]);
const [showColumnModal, setShowColumnModal] = useState(false);
const [tempSelectedColumns, setTempSelectedColumns] = useState([]);
const [tableFieldPreferences, setTableFieldPreferences] = useState([]);
const [selectedFields, setSelectedFields] = useState([]);
console.log(selectedAdditionalColumn, "selectedAdditionalColumn")
console.log(tempSelectedColumns, "tempSelectedColumns")
const CompId = getSession('CompId');
const BranchId = getSession('BranchId');
const AppId = getSession('AppId');
@ -149,19 +163,39 @@ const ProductForm = ({
setSelectedTaxId(TaxId);
}
}, [TaxData]);
useEffect(() => {
getFieldSetup();
}, [categoryId])
const getFieldSetup = async () => {
try {
const response = await dispatch(getFieldSetupData({ AppId, CompId, BranchId, categoryId, Type: "QA" })).unwrap();
if (response?.data?.statusCode === 1) {
console.log(response?.data?.data?.[0]?.ConfigDtl, "Field Setup Data");
setSelectedFields(response?.data?.data?.[0]?.ConfigDtl?.filter(c => c.ConfigId && c.Access === 'Y')?.map(c => c.ConfigId) || []);
setSelectedAdditionalColumn(response?.data?.data?.[0]?.ConfigDtl?.filter(c => c.ConfigId && c.Access === 'Y')?.map(c => c.ConfigName) || [])
setTableFieldPreferences(response?.data?.data?.[0]?.ConfigDtl?.map(c => ({
value: c.ConfigId,
label: c.ConfigName,
access: c.Access
})) || []);
}
} catch (error) {
console.error('Error fetching field setup:', error);
}
};
const onComplete = useCallback(() => {
setMessageData(null);
setMessageType(null);
}, []);
const openAdditionalDetails = () => {
if (OpenAdd) {
setOpenAdd(false);
} else {
setOpenAdd(true);
}
};
// const openAdditionalDetails = () => {
// if (OpenAdd) {
// setOpenAdd(false);
// } else {
// setOpenAdd(true);
// }
// };
const getApplicationPreference = async () => {
@ -367,7 +401,7 @@ const ProductForm = ({
setSelectedUom(null);
setSelectedTaxId(null);
handleCancelData();
setOpenAdd(false);
// setOpenAdd(false);
if (ProductSearchType != 'C') {
let data = {
CompId: CompId,
@ -444,6 +478,36 @@ const ProductForm = ({
}
}
const handleFieldSetupSubmit = async () => {
const postData = {
"AppId": AppId,
"CompId": CompId,
"BranchId": BranchId,
"Type": "QA",
"FormType": "Quick Add",
"TypeId": categoryId,
"ConfigDtl": selectedFields?.map((field) => ({
"ConfigId": field,
"Access": 'Y',
})),
"CreatedBy": UserId
}
const response = await dispatch(postFieldSetup(postData))?.unwrap();
if (response?.data?.statusCode === 1) {
setSelectedAdditionalColumn([...tempSelectedColumns]);
setShowColumnModal(false);
setTempSelectedColumns([])
setMessageType("success");
setMessageData(response?.data?.response);
await getFieldSetup();
} else {
setMessageType("error");
setMessageData("Failed to set up fields");
}
}
return (
<div>
<Messages
@ -455,6 +519,27 @@ const ProductForm = ({
<FormHeader title={'Quick Add'} />
</div>
<div>
<div style={{
display: 'flex', alignItems: 'center', gap: '10px', justifyContent: "flex-end"
, fontFamily: "Poppins", fontWeight: "500", textDecoration: "underline", cursor: "Pointer", width: "100%"
}}
onClick={() => {
setTempSelectedColumns([...selectedAdditionalColumn]);
setShowColumnModal(true);
}}>
<Tooltip title="Add Fields" placement="left">
{/* <span>Additional Fields</span> */}
<IoSettingsOutline
onClick={() => {
setTempSelectedColumns([...selectedAdditionalColumn]);
setShowColumnModal(true);
}}
style={{ cursor: 'pointer', fontSize: '20px' }}
/>
</Tooltip>
</div>
<Form ref={formRef} className="formDivAnt" onFinish={onFinish}>
<div>
<div className="productinputForm">
@ -667,30 +752,35 @@ const ProductForm = ({
</div>
)}
{OpenAdd && (
{true && (
<>
{taxField && <Form.Item name="TaxId">
<DropDowns
options={TaxData?.map((option) => ({
value: option.TaxId,
label: getOptionLabel(
option,
SelectedTaxId === option.TaxId
),
// label: option.TaxIdName + ' - ' + option.TaxPercentage + ' % ',
}))}
label="Tax"
className="field-DropDown"
isOnchanges={SelectedTaxId ? true : false}
onChangeFunction={handleTaxDropDownChange}
valueData={SelectedTaxId}
/>
<Tooltip title="Add Tax" placement="top">
<PlusCircleOutlined onClick={addTax} />
</Tooltip>
</Form.Item>}
{(taxField && selectedAdditionalColumn?.includes('Tax')) &&
<Form.Item name="TaxId">
<DropDowns
options={TaxData?.map((option) => ({
value: option.TaxId,
label: getOptionLabel(
option,
SelectedTaxId === option.TaxId
),
// label: option.TaxIdName + ' - ' + option.TaxPercentage + ' % ',
}))}
label="Tax"
className="field-DropDown"
isOnchanges={SelectedTaxId ? true : false}
onChangeFunction={handleTaxDropDownChange}
valueData={SelectedTaxId}
/>
</Form.Item>
}
{(taxField && selectedAdditionalColumn?.includes('Tax')) &&
<Form.Item name="TaxId">
<Tooltip title="Add Tax" placement="top">
<PlusCircleOutlined onClick={addTax} />
</Tooltip>
</Form.Item>}
{cessField && <Form.Item
{(cessField && selectedAdditionalColumn?.includes('Cess')) && <Form.Item
name="Cess"
rules={[
{
@ -708,13 +798,13 @@ const ProductForm = ({
<InputField autoComplete="off" label="Cess" />
</Form.Item>}
{hsnField &&
<Form.Item
name="HSNCode"
rules={[
{
{(hsnField && selectedAdditionalColumn?.includes('Hsn Code')) &&
<Form.Item
name="HSNCode"
rules={[
{
message: 'Please enter HSN',
},
},
]}
>
@ -779,32 +869,34 @@ const ProductForm = ({
}
<Form.Item
name="WhSalePrice"
rules={[
{
pattern: /^[0-9]\d*(\.\d+)?$/,
message: 'Please Enter WholeSale',
},
{
validator: async (_, value) => {
await validateSafeInput(value); // To block HTML tags & SQL keywords
return Promise.resolve();
{selectedAdditionalColumn?.includes('WholeSale') &&
<Form.Item
name="WhSalePrice"
rules={[
{
pattern: /^[0-9]\d*(\.\d+)?$/,
message: 'Please Enter WholeSale',
},
},
]}
>
<InputField
autoComplete="off"
label={<label>WholeSale</label>}
/>
</Form.Item>
{
validator: async (_, value) => {
await validateSafeInput(value); // To block HTML tags & SQL keywords
return Promise.resolve();
},
},
]}
>
<InputField
autoComplete="off"
label={<label>WholeSale</label>}
/>
</Form.Item>
}
</>
)}
</div>
<div className="prodAddDetails" onClick={openAdditionalDetails}>
{/* <div className="prodAddDetails" onClick={openAdditionalDetails}>
Additional Details
</div>
</div> */}
</div>
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
@ -823,6 +915,7 @@ const ProductForm = ({
open={OpenTaxModel}
footer={true}
buttonText="Submit"
destroyOnClose={true}
children={
<Form ref={formTaxRef} className="formDivAnt">
<div className="subRowFlex">
@ -901,6 +994,51 @@ const ProductForm = ({
handleSubmit={submitTax}
handleCancel={handleTax}
></DefaultModal>
<DefaultModal
title="Select Additional Fields"
open={showColumnModal}
footer={true}
buttonText="Apply"
destroyOnClose={true}
handleSubmit={handleFieldSetupSubmit}
handleCancel={() => {
setShowColumnModal(false);
setSelectedFields([...selectedAdditionalColumn.map(col =>
tableFieldPreferences?.find(pref => pref.label === col)?.value
).filter(Boolean)]);
setTempSelectedColumns([...selectedAdditionalColumn]);
}}
>
<div style={{ padding: '20px' }}>
{tableFieldPreferences?.map((option) => (
<div key={option.value} className='Customized_QuickAdd'>
<label>
<input
type="checkbox"
checked={selectedFields?.includes(option.value)}
onChange={(e) => {
const isChecked = e.target.checked;
setSelectedFields(prev =>
isChecked
? [...prev, option.value]
: prev.filter(item => item !== option.value)
);
setTempSelectedColumns(prev =>
isChecked
? [...prev, option.label]
: prev.filter(item => item !== option.label)
);
}}
style={{ marginRight: '8px' }}
/>
{option.label}
</label>
</div>
))}
</div>
</DefaultModal>
</div>
</div>
);

View File

@ -1,24 +1,9 @@
.cropUploadFieldSmall {
:where(
.css-dev-only-do-not-override-1v5z42l
).ant-upload-wrapper.ant-upload-picture-card-wrapper
.ant-upload-list.ant-upload-list-picture-card
.ant-upload-list-item-container,
:where(
.css-dev-only-do-not-override-1v5z42l
).ant-upload-wrapper.ant-upload-picture-circle-wrapper
.ant-upload-list.ant-upload-list-picture-card
.ant-upload-list-item-container,
:where(
.css-dev-only-do-not-override-1v5z42l
).ant-upload-wrapper.ant-upload-picture-card-wrapper
.ant-upload-list.ant-upload-list-picture-circle
.ant-upload-list-item-container,
:where(
.css-dev-only-do-not-override-1v5z42l
).ant-upload-wrapper.ant-upload-picture-circle-wrapper
.ant-upload-list.ant-upload-list-picture-circle
.ant-upload-list-item-container {
:where(.css-dev-only-do-not-override-1v5z42l).ant-upload-wrapper.ant-upload-picture-card-wrapper .ant-upload-list.ant-upload-list-picture-card .ant-upload-list-item-container,
:where(.css-dev-only-do-not-override-1v5z42l).ant-upload-wrapper.ant-upload-picture-circle-wrapper .ant-upload-list.ant-upload-list-picture-card .ant-upload-list-item-container,
:where(.css-dev-only-do-not-override-1v5z42l).ant-upload-wrapper.ant-upload-picture-card-wrapper .ant-upload-list.ant-upload-list-picture-circle .ant-upload-list-item-container,
:where(.css-dev-only-do-not-override-1v5z42l).ant-upload-wrapper.ant-upload-picture-circle-wrapper .ant-upload-list.ant-upload-list-picture-circle .ant-upload-list-item-container {
width: 65px;
height: 65px;
}
@ -125,6 +110,7 @@
.float-label {
margin-bottom: 0;
}
.ant-form-item-explain-error {
position: relative;
top: 16px;
@ -333,14 +319,14 @@
}
.viewerExcelupload .ant-table-wrapper,
.viewerExcelupload .ant-table-tbody > tr.ant-table-row > td {
.viewerExcelupload .ant-table-tbody>tr.ant-table-row>td {
padding: 4px;
font-size: 14px;
font-weight: 600;
}
.viewerExcelupload .ant-table-wrapper,
.viewerExcelupload .ant-table-thead > tr > th {
.viewerExcelupload .ant-table-thead>tr>th {
padding: 1px 8px !important;
}
@ -395,7 +381,7 @@
}
.productinputForm {
.ant-input-affix-wrapper > input.ant-input {
.ant-input-affix-wrapper>input.ant-input {
padding-top: 1.5rem;
}
}
@ -449,7 +435,7 @@
}
.product-Add-HSN {
.ant-input-affix-wrapper > input.ant-input {
.ant-input-affix-wrapper>input.ant-input {
padding: 12px 0 0 0 !important;
}
}
@ -490,7 +476,15 @@
}
.productFormproductImg{
.productFormproductImg {
width: 350px;
align-items: center;
}
.Customized_QuickAdd {
margin-bottom: 10px;
font-family: 'Poppins', sans-serif;
font-size: 16px;
font-weight: 400;
font-style: normal;
}