added a tax, brand, sub-category, category adding in turbo add
This commit is contained in:
parent
3e9b910616
commit
0268282eb1
|
|
@ -205,6 +205,17 @@ export const getProdSubCatData = createAsyncThunk(
|
|||
}
|
||||
);
|
||||
|
||||
export const getActiveProdSubCatData = createAsyncThunk(
|
||||
'product/getActiveProdSubCatData',
|
||||
async ({ AppId }) => {
|
||||
if (AppId !== null && AppId !== undefined) {
|
||||
return await axiosRetailInstanceData.get(
|
||||
`/configMaster?TypeName=Product Sub-Category&AppId=${AppId}`
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const getProductData = createAsyncThunk(
|
||||
'product/getProductData',
|
||||
async (ProdData) => {
|
||||
|
|
@ -661,6 +672,7 @@ const initialState = {
|
|||
uomData: [],
|
||||
prodCatData: [],
|
||||
prodSubCatData: [],
|
||||
allProdSubCatData: [],
|
||||
productData: [],
|
||||
prodTaxData: [],
|
||||
taxData: [],
|
||||
|
|
@ -723,6 +735,13 @@ const productSlice = createSlice({
|
|||
state.prodSubCatData = [];
|
||||
}
|
||||
}),
|
||||
builder.addCase(getActiveProdSubCatData.fulfilled, (state, action) => {
|
||||
if (action?.payload?.status) {
|
||||
state.allProdSubCatData = action?.payload?.data?.data;
|
||||
} else {
|
||||
state.allProdSubCatData = [];
|
||||
}
|
||||
}),
|
||||
builder.addCase(getBrandData.fulfilled, (state, action) => {
|
||||
if (action?.payload?.status) {
|
||||
state.brandData = action?.payload?.data?.data;
|
||||
|
|
@ -768,6 +787,8 @@ export const uomDataSelector = (state) => state.productPage?.uomData;
|
|||
export const prodCatDataSelector = (state) => state.productPage?.prodCatData;
|
||||
export const prodSubCatDataSelector = (state) =>
|
||||
state.productPage?.prodSubCatData;
|
||||
export const allProdSubCatDataSelector = (state) =>
|
||||
state.productPage?.allProdSubCatData;
|
||||
export const productDataSelector = (state) => state.productPage?.productData;
|
||||
export const brandDataSelector = (state) => state.productPage?.brandData;
|
||||
export const taxSelector = (state) => state.productPage?.taxData;
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ function BSReprint({ setOpenModel = () => {} }) {
|
|||
const SettingDataSelector = useSelector(PreferenceData);
|
||||
const salesBillEdit = useSelector(GlobalSalesBillEdit);
|
||||
const custDisable = useSelector(GlobalSelectedCustDisable);
|
||||
console.log(SettingDataSelector, 'SettingDataSelectoraa');
|
||||
console.log(DefReason, 'DefReason');
|
||||
const MobileA4Print = SettingDataSelector?.[0]?.SettingDtlDetails?.find(
|
||||
(setting) =>
|
||||
setting?.SettingIdName?.toLowerCase() === 'mobilea4' &&
|
||||
|
|
@ -292,6 +292,7 @@ function BSReprint({ setOpenModel = () => {} }) {
|
|||
} else {
|
||||
setDefReason({});
|
||||
reprintReasonRef?.current?.resetFields();
|
||||
reprintReasonRef?.current?.setFieldsValue({ SetAsDefault: false });
|
||||
setIsDuplicate('Original');
|
||||
}
|
||||
};
|
||||
|
|
@ -1054,7 +1055,7 @@ function BSReprint({ setOpenModel = () => {} }) {
|
|||
}
|
||||
}}
|
||||
>
|
||||
<AiFillPrinter className="BSC1Payment-icon" />
|
||||
<AiFillPrinter color={Object.keys(DefReason)?.length > 0 ? '#52c41a' : ''} className="BSC1Payment-icon" />
|
||||
Bill Without Token
|
||||
</button>
|
||||
<button
|
||||
|
|
@ -1072,13 +1073,14 @@ function BSReprint({ setOpenModel = () => {} }) {
|
|||
}
|
||||
}}
|
||||
>
|
||||
<AiFillPrinter className="BSC1Payment-icon" />
|
||||
<AiFillPrinter color={Object.keys(DefReason)?.length > 0 ? '#52c41a' : ''} className="BSC1Payment-icon" />
|
||||
Bill With Token
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<AiFillPrinter
|
||||
style={{ fontSize: '22px', cursor: 'pointer' }}
|
||||
color={Object.keys(DefReason)?.length > 0 ? '#52c41a' : ''}
|
||||
onClick={() => {
|
||||
setReprintData([row]);
|
||||
if (Object.keys(DefReason)?.length > 0) {
|
||||
|
|
|
|||
|
|
@ -86,6 +86,10 @@ import {
|
|||
import RackForm from '../Rack/RackForm.jsx';
|
||||
import { isMobile } from 'react-device-detect';
|
||||
import BarCodeScan from '../../Services/BarCodeScan.jsx';
|
||||
import AddCategoryModal from './Utils/AddCategoryModal.jsx';
|
||||
import AddSubCategoryModal from './Utils/AddSubCategoryModal.jsx';
|
||||
import AddBrandModal from './Utils/AddBrandModal.jsx';
|
||||
import AddTaxModal from './Utils/AddTaxModal.jsx';
|
||||
const subDirectory = import.meta.env.ENV_BASE_URL;
|
||||
|
||||
const ProductForm = ({ formType }) => {
|
||||
|
|
@ -1008,20 +1012,11 @@ const ProductForm = ({ formType }) => {
|
|||
setMessageData('Please add at least one Sub-Category.');
|
||||
return;
|
||||
}
|
||||
const subCategoryData = await formSubCategoryRef?.current?.validateFields();
|
||||
|
||||
const subCategoryTypeId = await dispatch(
|
||||
getConfigTypeData({ TypeName: 'Product Sub-Category' })
|
||||
).unwrap();
|
||||
|
||||
// const addSubCategoryData = {
|
||||
// TypeId: subCategoryTypeId?.data?.data?.[0]?.TypeId,
|
||||
// ConfigName: subCategoryData?.SubConfigName,
|
||||
// AlphaNumFId: AppId,
|
||||
// NumFId: SelectedCategory,
|
||||
// // SmallIcon: imageSubCategoryUrl,
|
||||
// CreatedBy: UserId,
|
||||
// }
|
||||
|
||||
const addSubCategoryData = {
|
||||
TypeId: subCategoryTypeId?.data?.data?.[0]?.TypeId || 0,
|
||||
ConfigDetails: Tabledata.map((item) => ({
|
||||
|
|
@ -1980,67 +1975,7 @@ const ProductForm = ({ formType }) => {
|
|||
// setOpenTour(false)
|
||||
// }
|
||||
// }
|
||||
const Columns = [
|
||||
{
|
||||
title: 'Sl.No',
|
||||
key: 'sno',
|
||||
align: 'center',
|
||||
render: (text, object, index) => (
|
||||
<a style={{ color: 'black' }}>{(page - 1) * 10 + index + 1}</a>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'SubCategory Name',
|
||||
dataIndex: 'SubCategory',
|
||||
key: 'SubCategory',
|
||||
editable: true,
|
||||
render: (text) => <a style={{ color: 'black' }}>{text}</a>,
|
||||
filteredValue: [searchedText],
|
||||
onFilter: (value, record) => {
|
||||
return (
|
||||
String(record.TableName)
|
||||
?.toLowerCase()
|
||||
?.includes(value?.toLowerCase()) ||
|
||||
String(record.TableName)
|
||||
?.toLowerCase()
|
||||
?.includes(value?.toLowerCase()) ||
|
||||
String(record.ChairCount)
|
||||
?.toLowerCase()
|
||||
?.includes(value?.toLowerCase()) ||
|
||||
String(record.ChairLevelSer)
|
||||
?.toLowerCase()
|
||||
?.includes(value?.toLowerCase()) ||
|
||||
String(record.DineInTypeName)
|
||||
?.toLowerCase()
|
||||
?.includes(value?.toLowerCase())
|
||||
);
|
||||
},
|
||||
sorter: (a, b) => a?.ProdName?.length - b?.ProdName?.length,
|
||||
sortOrder:
|
||||
sortedInfo.columnKey === 'SubCategory' ? sortedInfo.order : null,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
key: 'Action',
|
||||
dataIndex: 'Action',
|
||||
align: 'center',
|
||||
render: (_, record, index) => (
|
||||
// Tabledata.length >= 1 ? (
|
||||
<Space size="middle">
|
||||
<a>
|
||||
{/* {record.ActiveStatus === "A" ? ( */}
|
||||
<DeleteFilled
|
||||
style={{
|
||||
color: '#FF4D4F',
|
||||
}}
|
||||
onClick={() => statusFormatter(record, index)}
|
||||
/>
|
||||
</a>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const Onchangeoffertype = (a) => {
|
||||
setDiscountType(a);
|
||||
|
||||
|
|
@ -2634,8 +2569,7 @@ const ProductForm = ({ formType }) => {
|
|||
<div className="productonlineImg">
|
||||
{imagedata?.map((item, index) => (
|
||||
<div
|
||||
className={`singleimg ${
|
||||
selectedImage === item ? 'selected' : ''
|
||||
className={`singleimg ${selectedImage === item ? 'selected' : ''
|
||||
}`}
|
||||
key={index}
|
||||
onClick={() => setSelectedImage(item)}
|
||||
|
|
@ -4063,184 +3997,17 @@ const ProductForm = ({ formType }) => {
|
|||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
<DefaultModal
|
||||
title="Add Category"
|
||||
<AddCategoryModal
|
||||
ref={formCategoryRef}
|
||||
open={OpenCategoryModel}
|
||||
footer={true}
|
||||
buttonText="Submit"
|
||||
children={
|
||||
<Form ref={formCategoryRef} className="formDivAnt">
|
||||
<div className="ProSubRowFlex">
|
||||
<div className="subColumnFlex">
|
||||
<div>
|
||||
<Form.Item
|
||||
name="ConfigName"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
pattern: /^(?!\s*$).+/,
|
||||
message: 'Please Enter Category Name',
|
||||
},
|
||||
{
|
||||
validator: async (_, value) => {
|
||||
await validateSafeInput(value);
|
||||
|
||||
if (value && value.length > 50) {
|
||||
return Promise.reject(
|
||||
'Category Name should not exceed 50 characters'
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputField
|
||||
label={
|
||||
<label class="required">Category Name</label>
|
||||
}
|
||||
autocomplete="off"
|
||||
close={setOpenCategoryModel}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="subColumnFlex">
|
||||
<div className="upload_btn">
|
||||
<p>Upload Icon</p>
|
||||
<br></br>
|
||||
<div style={{ width: 'max-content' }}>
|
||||
<Imageupload
|
||||
singleImage={true}
|
||||
updateImageUrl={updateCategoryImageUrl}
|
||||
ImageLink={imageCategoryUrl ? imageCategoryUrl : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
}
|
||||
handleSubmit={submitCategory}
|
||||
handleCancel={handleCategory}
|
||||
></DefaultModal>
|
||||
|
||||
<DefaultModal
|
||||
title="Add SubCategory"
|
||||
<AddSubCategoryModal
|
||||
open={OpenSubCategoryModel}
|
||||
footer={true}
|
||||
buttonText="Submit"
|
||||
handleSubmit={submitSubCategory}
|
||||
handleCancel={handleSubCategory}
|
||||
children={
|
||||
<Form ref={formSubCategoryRef} className="formDivAnt">
|
||||
<div className="ProSubRowFlex">
|
||||
<div className="subColumnFlex">
|
||||
<div className="subProdCatData">
|
||||
<Form.Item
|
||||
name="CategoryId"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select a Category',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<DropDowns
|
||||
options={ProdCatData?.map((option) => ({
|
||||
value: option.ConfigId,
|
||||
label: option.ConfigName,
|
||||
}))}
|
||||
label={<label class="required">Category</label>}
|
||||
className="field-DropDown"
|
||||
onChangeFunction={handleCatDropDownChange}
|
||||
valueData={SelectedCategory}
|
||||
disabled={Tabledata.length >= 1}
|
||||
close={setOpenSubCategoryModel}
|
||||
categoryData={ProdCatData}
|
||||
ref={formSubCategoryRef}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
{/* <div className="upload_btn">
|
||||
<p>Upload Icon</p>
|
||||
<br></br>
|
||||
<Imageupload singleImage={true} updateImageUrl={updateSubCategoryImageUrl} ImageLink={imageSubCategoryUrl ? imageSubCategoryUrl : ""} />
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="subColumnFlex">
|
||||
<div>
|
||||
<Form.Item name="SubConfigName">
|
||||
<InputField
|
||||
label={
|
||||
<label class="required">SubCategory Name</label>
|
||||
}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ fontSize: '25px' }}>
|
||||
<Tooltip title="Add Products" placement="top">
|
||||
<PlusCircleOutlined
|
||||
className="product-add-iconMargin"
|
||||
onClick={async () => {
|
||||
try {
|
||||
const values =
|
||||
await formSubCategoryRef.current?.validateFields(
|
||||
['CategoryId', 'SubConfigName']
|
||||
);
|
||||
const trimmedValue = values?.SubConfigName
|
||||
? values.SubConfigName.trim()
|
||||
: '';
|
||||
if (!values?.CategoryId) {
|
||||
throw new Error('Please Select Category');
|
||||
}
|
||||
if (!trimmedValue) {
|
||||
throw new Error('SubCategory Name is required');
|
||||
}
|
||||
await validateSafeInput(trimmedValue);
|
||||
if (trimmedValue.length > 50) {
|
||||
throw new Error(
|
||||
'SubCategory Name should not exceed 50 characters'
|
||||
);
|
||||
}
|
||||
!editDataval ? handleEdit() : handleadd();
|
||||
} catch (error) {
|
||||
message.error(
|
||||
error.message || 'Please fill required fields'
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="Quotation-tables"
|
||||
style={{
|
||||
maxwidth: '500px',
|
||||
overflowX: 'auto',
|
||||
scrollbarWidth: 'thin',
|
||||
}}
|
||||
>
|
||||
<Table
|
||||
bordered
|
||||
// components={components}
|
||||
// rowClassName="editable-row"
|
||||
columns={Columns}
|
||||
dataSource={Tabledata}
|
||||
// pagination={handlePageChange}
|
||||
// onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
}
|
||||
></DefaultModal>
|
||||
|
||||
<DefaultModal
|
||||
open={OpenRackModel}
|
||||
footer={false}
|
||||
|
|
@ -4250,195 +4017,18 @@ const ProductForm = ({ formType }) => {
|
|||
}
|
||||
></DefaultModal>
|
||||
|
||||
<DefaultModal
|
||||
title="Add Brand"
|
||||
<AddBrandModal
|
||||
open={OpenBrandModel}
|
||||
footer={true}
|
||||
buttonText="Submit"
|
||||
handleSubmit={submitBrand}
|
||||
handleCancel={handleBrand}
|
||||
children={
|
||||
<Form ref={formBrandRef} className="formDivAnt">
|
||||
<div className="ProSubRowFlex">
|
||||
<div className="subColumnFlex">
|
||||
<div className="subProdCatData">
|
||||
<Form.Item
|
||||
name="SubCategoryId"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please Select Sub-Category',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<DropDowns
|
||||
options={ProdSubCatData?.map((option) => ({
|
||||
value: option.ConfigId,
|
||||
label: option.ConfigName,
|
||||
}))}
|
||||
label={<label class="required">Sub-Category</label>}
|
||||
className="field-DropDown"
|
||||
onChangeFunction={handleSubCatDropDownChange}
|
||||
valueData={SelectedSubCategory}
|
||||
close={setOpenBrandModel}
|
||||
subCategories={ProdSubCatData}
|
||||
ref={formBrandRef}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<div className="upload_btn">
|
||||
<p>Upload Icon</p>
|
||||
<br></br>
|
||||
<div style={{ width: 'max-content' }}>
|
||||
<Imageupload
|
||||
singleImage={true}
|
||||
updateImageUrl={updateBrandImageUrl}
|
||||
ImageLink={imageBrandUrl ? imageBrandUrl : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="subColumnFlex">
|
||||
<div>
|
||||
<Form.Item
|
||||
name="SubConfigName"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
pattern: /^(?!\s*$).+/,
|
||||
message: 'Please Enter Brand Name',
|
||||
},
|
||||
{
|
||||
validator: async (_, value) => {
|
||||
await validateSafeInput(value);
|
||||
|
||||
if (value && value.length > 50) {
|
||||
return Promise.reject(
|
||||
'Brand Name should not exceed 50 characters'
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputField
|
||||
label={<label class="required">Brand Name</label>}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
}
|
||||
></DefaultModal>
|
||||
|
||||
<DefaultModal
|
||||
title="Add Tax"
|
||||
<AddTaxModal
|
||||
open={OpenTaxModel}
|
||||
footer={true}
|
||||
buttonText="Submit"
|
||||
children={
|
||||
<Form ref={formTaxRef} className="formDivAnt">
|
||||
<div className="ProSubRowFlex">
|
||||
<Form.Item name="TaxName">
|
||||
<DropDowns
|
||||
options={ProdTaxData?.map((option) => ({
|
||||
value: option.ConfigId,
|
||||
label: option.ConfigName,
|
||||
}))}
|
||||
label="Tax Name"
|
||||
className="field-DropDown"
|
||||
onChangeFunction={handleTaxNameDropDownChange}
|
||||
valueData={SelectedTaxNameId}
|
||||
close={setOpenTaxModel}
|
||||
prodTaxData={ProdTaxData}
|
||||
ref={formTaxRef}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="TaxPercentage"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
pattern: /^(?:100(?:\.0+)?|\d{1,2}(?:\.\d+)?)$/,
|
||||
message: 'Please Enter Valid Percentage',
|
||||
},
|
||||
{
|
||||
validator: async (_, value) => {
|
||||
await validateSafeInput(value); // To block HTML tags & SQL keywords
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputField
|
||||
autoComplete="off"
|
||||
label="Tax Percentage"
|
||||
inputMode="decimal"
|
||||
onInput={(e) => {
|
||||
let cleanedValue = e.target.value.replace(
|
||||
/[^0-9.]/g,
|
||||
''
|
||||
); // Remove invalid characters
|
||||
const parts = cleanedValue.split('.');
|
||||
|
||||
if (cleanedValue.startsWith('.')) {
|
||||
cleanedValue = '0' + cleanedValue;
|
||||
}
|
||||
|
||||
e.target.value =
|
||||
parts.length > 2
|
||||
? `${parts[0]}.${parts.slice(1).join('')}`
|
||||
: cleanedValue;
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="Reference"
|
||||
rules={[
|
||||
{
|
||||
pattern: /^(?!\s*$).+/,
|
||||
message: 'Please Enter Reference',
|
||||
},
|
||||
{
|
||||
validator: async (_, value) => {
|
||||
await validateSafeInput(value); // To block HTML tags & SQL keywords
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputField label="Reference No" autoComplete="off" />
|
||||
</Form.Item>
|
||||
<div>
|
||||
<label className="required">Effective From :</label>
|
||||
</div>
|
||||
|
||||
<Form.Item
|
||||
name="EffectiveFrom"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please Enter Effective From',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<DatePic
|
||||
field="EffectiveFrom"
|
||||
type="number"
|
||||
min={1}
|
||||
max={100}
|
||||
fieldState={true}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Form>
|
||||
}
|
||||
handleSubmit={submitTax}
|
||||
handleCancel={handleTax}
|
||||
></DefaultModal>
|
||||
|
||||
<Modal
|
||||
title="Is this the product you were searching for?"
|
||||
open={QrcodeExistsDataOpen}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
getCommonAppPreference,
|
||||
} from '../../Features/BrachLogin/BranchLogin.js';
|
||||
import {
|
||||
allProdSubCatDataSelector,
|
||||
bulkpostdata,
|
||||
getBrandData,
|
||||
getFieldSetupData,
|
||||
|
|
@ -23,6 +24,7 @@ import {
|
|||
postFieldSetup,
|
||||
prodCatDataSelector,
|
||||
prodSubCatDataSelector,
|
||||
prodTaxDataSelector,
|
||||
productTypeDataSelector,
|
||||
putProductData,
|
||||
uomDataSelector,
|
||||
|
|
@ -57,6 +59,11 @@ import { BiBarcodeReader } from 'react-icons/bi';
|
|||
import BarcodeScanner from '../BookingScreen/Components/UtillComponents/BarcodeScanner.jsx';
|
||||
import BarcodeScannerTurbo from '../BookingScreen/Components/UtillComponents/BarcodeScannerTurbo.jsx';
|
||||
import BarCodeScan from '../../Services/BarCodeScan.jsx';
|
||||
import AddCategoryModal from './Utils/AddCategoryModal.jsx';
|
||||
import { getConfigTypeData, postConfiguration } from '../../Features/ConfigMasterPage/ConfigMasterPage.js';
|
||||
import AddSubCategoryModal from './Utils/AddSubCategoryModal.jsx';
|
||||
import AddBrandModal from './Utils/AddBrandModal.jsx';
|
||||
import AddTaxModal from './Utils/AddTaxModal.jsx';
|
||||
|
||||
const subDirectory = import.meta.env.ENV_BASE_URL;
|
||||
const items = [
|
||||
|
|
@ -153,9 +160,13 @@ const createValidationRules = (products) => ({
|
|||
const TurboAddForm = () => {
|
||||
const dispatch = useDispatch();
|
||||
// Redux selectors
|
||||
const taxData = useSelector(taxSelector);
|
||||
const taxRef = useRef();
|
||||
const brandRef = useRef();
|
||||
const categoryRef = useRef();
|
||||
const subCategoryRef = useRef();
|
||||
const scannerRef = useRef();
|
||||
console.log(taxData, 'taxData');
|
||||
const prodTaxData = useSelector(prodTaxDataSelector);
|
||||
const taxData = useSelector(taxSelector);
|
||||
const UomData = useSelector(uomDataSelector);
|
||||
const productTypeData = useSelector(productTypeDataSelector);
|
||||
const ApplicationPreferenceData = useSelector(ApplicationPreferences);
|
||||
|
|
@ -164,8 +175,8 @@ const TurboAddForm = () => {
|
|||
)?.PreferredCatId;
|
||||
const prodCategories = useSelector(prodCatDataSelector);
|
||||
const prodSubCategories = useSelector(prodSubCatDataSelector);
|
||||
const allProdSubCategories = useSelector(allProdSubCatDataSelector);
|
||||
const searchText = useSelector(GlobalSearchData);
|
||||
|
||||
// Session data
|
||||
const AppId = useMemo(() => getSession('AppId'), []);
|
||||
const CompId = useMemo(() => getSession('CompId'), []);
|
||||
|
|
@ -173,6 +184,11 @@ const TurboAddForm = () => {
|
|||
const UserId = useMemo(() => getSession('UserId'), []);
|
||||
|
||||
// State
|
||||
const [openTaxModal, setOpenTaxModal] = useState(false);
|
||||
const [openCategoryModal, setOpenCategoryModal] = useState(false);
|
||||
const [openSubCategoryModal, setOpenSubCategoryModal] = useState(false);
|
||||
const [openBrandModal, setOpenBrandModal] = useState(false);
|
||||
const [imageCategoryUrl, setImageCategoryUrl] = useState('');
|
||||
const [voiceData, setVoiceData] = useState(null);
|
||||
const [messageType, setMessageType] = useState(null);
|
||||
const [messageData, setMessageData] = useState(null);
|
||||
|
|
@ -372,6 +388,8 @@ const TurboAddForm = () => {
|
|||
[UomPreference, taxData, prodCategories, prodSubCategories]
|
||||
);
|
||||
|
||||
console.log(selectOptions?.tax, taxData, "selectOptions tax");
|
||||
|
||||
// Filtered products for search
|
||||
const filteredProducts = useMemo(() => {
|
||||
if (!searchTerm.trim()) return products;
|
||||
|
|
@ -2022,6 +2040,12 @@ const TurboAddForm = () => {
|
|||
field="category"
|
||||
options={defaultCategories}
|
||||
onSubmit={handleDefaultSetting}
|
||||
triggerModal={setOpenCategoryModal}
|
||||
Component={<AddCategoryModal
|
||||
ref={categoryRef}
|
||||
open={openCategoryModal}
|
||||
close={setOpenCategoryModal}
|
||||
/>}
|
||||
/>
|
||||
</th>
|
||||
{hasField('Sub Category') && (
|
||||
|
|
@ -2039,6 +2063,15 @@ const TurboAddForm = () => {
|
|||
? `Selected Category : ${defaultCategories?.find((cat) => cat.value === selectedDefaultCategory)?.label}`
|
||||
: null
|
||||
}
|
||||
triggerModal={setOpenSubCategoryModal}
|
||||
Component={<AddSubCategoryModal
|
||||
open={openSubCategoryModal}
|
||||
close={setOpenSubCategoryModal}
|
||||
categoryData={prodCategories}
|
||||
products={products}
|
||||
ref={subCategoryRef}
|
||||
setProducts={setProducts}
|
||||
/>}
|
||||
/>
|
||||
</th>
|
||||
)}
|
||||
|
|
@ -2060,6 +2093,16 @@ const TurboAddForm = () => {
|
|||
checkBoxHeading={
|
||||
'Apply to all selected sub-category rows'
|
||||
}
|
||||
triggerModal={setOpenBrandModal}
|
||||
needAllSubCategories={true}
|
||||
Component={<AddBrandModal
|
||||
open={openBrandModal}
|
||||
close={setOpenBrandModal}
|
||||
subCategories={allProdSubCategories}
|
||||
products={products}
|
||||
setProducts={setProducts}
|
||||
ref={brandRef}
|
||||
/>}
|
||||
/>
|
||||
</th>
|
||||
)}
|
||||
|
|
@ -2090,6 +2133,14 @@ const TurboAddForm = () => {
|
|||
options={selectOptions.tax}
|
||||
onSubmit={handleDefaultSetting}
|
||||
currentDefault={defaults.tax}
|
||||
triggerModal={setOpenTaxModal}
|
||||
Component={<AddTaxModal
|
||||
open={openTaxModal}
|
||||
close={setOpenTaxModal}
|
||||
prodTaxData={prodTaxData}
|
||||
ref={taxRef}
|
||||
flag={true}
|
||||
/>}
|
||||
/>
|
||||
</th>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,204 @@
|
|||
import { Form } from 'antd';
|
||||
import { DefaultModal } from '../../../Components/Modal/DefaultModal';
|
||||
import { getSession, validateSafeInput } from '../../../Services/Others';
|
||||
import { DropDowns } from '../../../Components/Forms/DropDown';
|
||||
import Imageupload from '../../../Components/Forms/Upload.jsx';
|
||||
import { InputField } from '../../../../ownLib/my-ui-lib';
|
||||
import { forwardRef, useCallback, useState } from 'react';
|
||||
import { getConfigTypeData, postConfiguration } from '../../../Features/ConfigMasterPage/ConfigMasterPage.js';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { getBrandData } from '../../../Features/ProductPage/ProductPage.js';
|
||||
import { Messages } from '../../../Components/Notifications/Messages.jsx';
|
||||
|
||||
const AddBrandModal = forwardRef((
|
||||
{
|
||||
open = false,
|
||||
close = () => { },
|
||||
subCategories = [],
|
||||
products = [],
|
||||
setProducts = () => { },
|
||||
}, ref) => {
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const AppId = getSession('AppId');
|
||||
const UserId = getSession('UserId');
|
||||
|
||||
const [messageType, setMessageType] = useState(null);
|
||||
const [messageData, setMessageData] = useState(null);
|
||||
const [imageBrandUrl, setBrandImageUrl] = useState('');
|
||||
const [selectedSubCategory, setSelectedSubCategory] = useState(null);
|
||||
|
||||
const handleBrandModalClose = () => {
|
||||
close(false);
|
||||
setBrandImageUrl('');
|
||||
setSelectedSubCategory(null);
|
||||
ref?.current?.resetFields();
|
||||
};
|
||||
|
||||
const handleBrandImageUrlUpdate = (url) => {
|
||||
setBrandImageUrl(url);
|
||||
};
|
||||
|
||||
const handleSubCatDropDownChange = (value) => {
|
||||
setSelectedSubCategory(value);
|
||||
ref?.current?.setFieldsValue({ SubCategoryId: value });
|
||||
};
|
||||
|
||||
const handleBrandSubmit = async () => {
|
||||
|
||||
const subBrandData = await ref?.current?.validateFields();
|
||||
const subBrandTypeId = await dispatch(
|
||||
getConfigTypeData({ TypeName: 'Product Brand' })
|
||||
).unwrap();
|
||||
|
||||
const addBrandData = {
|
||||
TypeId: subBrandTypeId?.data?.data?.[0]?.TypeId,
|
||||
ConfigName: subBrandData?.SubConfigName,
|
||||
AlphaNumFId: AppId,
|
||||
NumFId: selectedSubCategory,
|
||||
SmallIcon: imageBrandUrl,
|
||||
CreatedBy: UserId,
|
||||
};
|
||||
|
||||
const response = await dispatch(postConfiguration(addBrandData)).unwrap();
|
||||
if (response?.data?.statusCode == 1) {
|
||||
setBrandImageUrl('');
|
||||
setMessageType('success');
|
||||
setMessageData(response?.data?.response);
|
||||
close(false);
|
||||
const brandResponse = await dispatch(getBrandData({ ConfigId: selectedSubCategory }))?.unwrap();
|
||||
ref?.current?.resetFields();
|
||||
if (products?.length > 0) {
|
||||
const hasTheSubCategory = products.some(product => product.subCategory === selectedSubCategory);
|
||||
if (hasTheSubCategory) {
|
||||
if (brandResponse?.data?.statusCode == 1) {
|
||||
const brands = brandResponse?.data?.data || [];
|
||||
const availableBrands = brands?.filter(
|
||||
(subCat) =>
|
||||
subCat.NumFId === parseInt(selectedSubCategory) && !!subCat?.ConfigName
|
||||
)?.map((subCat) => ({
|
||||
label: subCat.ConfigName,
|
||||
value: subCat.ConfigId,
|
||||
}));
|
||||
|
||||
const updatedProducts = products?.map((product) => {
|
||||
if (product.subCategory === selectedSubCategory) {
|
||||
return {
|
||||
...product,
|
||||
availableBrands,
|
||||
};
|
||||
}
|
||||
return product;
|
||||
});
|
||||
|
||||
setProducts(updatedProducts);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
setSelectedSubCategory(null);
|
||||
} else {
|
||||
setBrandImageUrl('');
|
||||
setSelectedSubCategory(null);
|
||||
setMessageType('error');
|
||||
setMessageData(response?.data?.response);
|
||||
ref?.current?.resetFields();
|
||||
}
|
||||
};
|
||||
|
||||
const onComplete = useCallback(() => {
|
||||
setMessageType(null);
|
||||
setMessageData(null);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Messages messageData={messageData} messageType={messageType} onComplete={onComplete} />
|
||||
<DefaultModal
|
||||
title="Add Brand"
|
||||
open={open}
|
||||
footer={true}
|
||||
buttonText="Submit"
|
||||
handleSubmit={handleBrandSubmit}
|
||||
handleCancel={handleBrandModalClose}
|
||||
children={
|
||||
<Form ref={ref} className="formDivAnt">
|
||||
<div className="ProSubRowFlex">
|
||||
<div className="subColumnFlex">
|
||||
<div className="subProdCatData">
|
||||
<Form.Item
|
||||
name="SubCategoryId"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please Select Sub-Category',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<DropDowns
|
||||
options={subCategories?.map((option) => ({
|
||||
value: option.ConfigId,
|
||||
label: option.ConfigName,
|
||||
}))}
|
||||
label={<label class="required">Sub-Category</label>}
|
||||
className="field-DropDown"
|
||||
onChangeFunction={handleSubCatDropDownChange}
|
||||
valueData={selectedSubCategory}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<div className="upload_btn">
|
||||
<p>Upload Icon</p>
|
||||
<br></br>
|
||||
<div style={{ width: 'max-content' }}>
|
||||
<Imageupload
|
||||
singleImage={true}
|
||||
updateImageUrl={handleBrandImageUrlUpdate}
|
||||
ImageLink={imageBrandUrl ? imageBrandUrl : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="subColumnFlex">
|
||||
<div>
|
||||
<Form.Item
|
||||
name="SubConfigName"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
pattern: /^(?!\s*$).+/,
|
||||
message: 'Please Enter Brand Name',
|
||||
},
|
||||
{
|
||||
validator: async (_, value) => {
|
||||
await validateSafeInput(value);
|
||||
|
||||
if (value && value.length > 50) {
|
||||
return Promise.reject(
|
||||
'Brand Name should not exceed 50 characters'
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputField
|
||||
label={<label class="required">Brand Name</label>}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
});
|
||||
|
||||
export default AddBrandModal
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
import { forwardRef, useCallback, useState } from "react";
|
||||
import Imageupload from '../../../Components/Forms/Upload.jsx';
|
||||
import { DefaultModal } from "../../../Components/Modal/DefaultModal";
|
||||
import { InputField } from "../../../Components/Forms/InputField";
|
||||
import { Form } from "antd";
|
||||
import { getSession, validateSafeInput } from "../../../Services/Others.js";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { getConfigTypeData, postConfiguration } from "../../../Features/ConfigMasterPage/ConfigMasterPage.js";
|
||||
import { getProdCatData } from "../../../Features/ProductPage/ProductPage.js";
|
||||
import { Messages } from "../../../Components/Notifications/Messages.jsx";
|
||||
|
||||
const AddCategoryModal = forwardRef((
|
||||
{
|
||||
open = false,
|
||||
close = () => { },
|
||||
}, ref) => {
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const AppId = getSession('AppId');
|
||||
const UserId = getSession('UserId');
|
||||
|
||||
const [messageType, setMessageType] = useState('');
|
||||
const [messageData, setMessageData] = useState('');
|
||||
|
||||
const [imageCategoryUrl, setImageCategoryUrl] = useState('');
|
||||
|
||||
const handleImageCategoryUrlUpdate = (url) => {
|
||||
setImageCategoryUrl(url);
|
||||
};
|
||||
|
||||
const handleSubmitCategory = async () => {
|
||||
try {
|
||||
const values = await ref?.current?.validateFields();
|
||||
|
||||
const categoryTypeId = await dispatch(
|
||||
getConfigTypeData({ TypeName: 'Product Category' })
|
||||
).unwrap();
|
||||
|
||||
const data = {
|
||||
TypeId: categoryTypeId?.data?.data?.[0]?.TypeId,
|
||||
ConfigName: values?.ConfigName,
|
||||
AlphaNumFId: AppId,
|
||||
SmallIcon: imageCategoryUrl,
|
||||
CreatedBy: UserId,
|
||||
};
|
||||
const response = await dispatch(postConfiguration(data)).unwrap();
|
||||
if (response?.data?.statusCode === 1) {
|
||||
await dispatch(getProdCatData({ AppId: AppId }));
|
||||
close(false);
|
||||
setImageCategoryUrl('');
|
||||
setMessageType('success');
|
||||
setMessageData(response?.data?.response);
|
||||
ref?.current?.resetFields();
|
||||
} else {
|
||||
setMessageType('error');
|
||||
setMessageData(response?.data?.response);
|
||||
}
|
||||
return;
|
||||
}
|
||||
catch (error) {
|
||||
console.log('Error:', error?.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const handleModalClose = () => {
|
||||
close(false);
|
||||
setImageCategoryUrl('');
|
||||
ref?.current?.resetFields();
|
||||
};
|
||||
|
||||
const onComplete = useCallback(() => {
|
||||
setMessageData(null);
|
||||
setMessageType(null);
|
||||
}, []);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Messages messageData={messageData} messageType={messageType} onComplete={onComplete} />
|
||||
<DefaultModal
|
||||
title="Add Category"
|
||||
open={open}
|
||||
footer={true}
|
||||
buttonText="Submit"
|
||||
children={
|
||||
<Form ref={ref} className="formDivAnt">
|
||||
<div className="ProSubRowFlex">
|
||||
<div className="subColumnFlex">
|
||||
<div>
|
||||
<Form.Item
|
||||
name="ConfigName"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
pattern: /^(?!\s*$).+/,
|
||||
message: 'Please Enter Category Name',
|
||||
},
|
||||
{
|
||||
validator: async (_, value) => {
|
||||
await validateSafeInput(value);
|
||||
|
||||
if (value && value.length > 50) {
|
||||
return Promise.reject(
|
||||
'Category Name should not exceed 50 characters'
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputField
|
||||
label={
|
||||
<label class="required">Category Name</label>
|
||||
}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="subColumnFlex">
|
||||
<div className="upload_btn">
|
||||
<p>Upload Icon</p>
|
||||
<br></br>
|
||||
<div style={{ width: 'max-content' }}>
|
||||
<Imageupload
|
||||
singleImage={true}
|
||||
updateImageUrl={handleImageCategoryUrlUpdate}
|
||||
ImageLink={imageCategoryUrl ? imageCategoryUrl : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
}
|
||||
handleSubmit={handleSubmitCategory}
|
||||
handleCancel={handleModalClose}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
});
|
||||
|
||||
export default AddCategoryModal;
|
||||
|
|
@ -0,0 +1,337 @@
|
|||
import { Form, Table } from 'antd';
|
||||
import { forwardRef, useCallback, useState } from 'react';
|
||||
import { DropDowns } from '../../../Components/Forms/DropDown';
|
||||
import { getSession, validateSafeInput } from '../../../Services/Others';
|
||||
import { InputField } from '../../../Components/Forms/InputField';
|
||||
import { PlusCircleOutlined, DeleteFilled } from '@ant-design/icons';
|
||||
import { Tooltip, message, Space } from 'antd';
|
||||
import { DefaultModal } from '../../../Components/Modal/DefaultModal';
|
||||
import { Messages } from '../../../Components/Notifications/Messages';
|
||||
import { getProdSubCatData, postConfigurationmultiple } from '../../../Features/ProductPage/ProductPage';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { getConfigTypeData } from '../../../Features/ConfigMasterPage/ConfigMasterPage';
|
||||
|
||||
const AddSubCategoryModal = forwardRef((
|
||||
{
|
||||
open = false,
|
||||
close = () => { },
|
||||
categoryData = [],
|
||||
products = [],
|
||||
setProducts = () => { },
|
||||
flag = false,
|
||||
}, ref) => {
|
||||
|
||||
const AppId = getSession('AppId');
|
||||
const UserId = getSession('UserId');
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [page, setPage] = useState(1);
|
||||
const [searchedText, setSearchedText] = useState('');
|
||||
const [sortedInfo, setSortedInfo] = useState({});
|
||||
const [editDataval, setEditDataVal] = useState(true);
|
||||
const [messageType, setMessageType] = useState('');
|
||||
const [messageData, setMessageData] = useState('');
|
||||
const [imageSubCategoryUrl, setSubCategoryImageUrl] = useState('');
|
||||
|
||||
const [selectedCategory, setSelectedCategory] = useState(null);
|
||||
const [tableData, setTableData] = useState([]);
|
||||
|
||||
const Columns = [
|
||||
{
|
||||
title: 'Sl.No',
|
||||
key: 'sno',
|
||||
align: 'center',
|
||||
render: (text, object, index) => (
|
||||
<a style={{ color: 'black' }}>{(page - 1) * 10 + index + 1}</a>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'SubCategory Name',
|
||||
dataIndex: 'SubCategory',
|
||||
key: 'SubCategory',
|
||||
editable: true,
|
||||
render: (text) => <a style={{ color: 'black' }}>{text}</a>,
|
||||
filteredValue: [searchedText],
|
||||
onFilter: (value, record) => {
|
||||
return (
|
||||
String(record.TableName)
|
||||
?.toLowerCase()
|
||||
?.includes(value?.toLowerCase()) ||
|
||||
String(record.TableName)
|
||||
?.toLowerCase()
|
||||
?.includes(value?.toLowerCase()) ||
|
||||
String(record.ChairCount)
|
||||
?.toLowerCase()
|
||||
?.includes(value?.toLowerCase()) ||
|
||||
String(record.ChairLevelSer)
|
||||
?.toLowerCase()
|
||||
?.includes(value?.toLowerCase()) ||
|
||||
String(record.DineInTypeName)
|
||||
?.toLowerCase()
|
||||
?.includes(value?.toLowerCase())
|
||||
);
|
||||
},
|
||||
sorter: (a, b) => a?.ProdName?.length - b?.ProdName?.length,
|
||||
sortOrder:
|
||||
sortedInfo.columnKey === 'SubCategory' ? sortedInfo.order : null,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
key: 'Action',
|
||||
dataIndex: 'Action',
|
||||
align: 'center',
|
||||
render: (_, record, index) => (
|
||||
// Tabledata.length >= 1 ? (
|
||||
<Space size="middle">
|
||||
<a>
|
||||
{/* {record.ActiveStatus === "A" ? ( */}
|
||||
<DeleteFilled
|
||||
style={{
|
||||
color: '#FF4D4F',
|
||||
}}
|
||||
onClick={() => statusFormatter(record, index)}
|
||||
/>
|
||||
</a>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const handleAddSubCategory = async () => {
|
||||
const subCategoryData = await ref?.current?.validateFields();
|
||||
|
||||
const alreadyExists = tableData.some(
|
||||
(item) => item.SubCategory === subCategoryData?.SubConfigName
|
||||
);
|
||||
|
||||
if (!alreadyExists) {
|
||||
setTableData((prev) => {
|
||||
const newIndex = prev.length + 1;
|
||||
|
||||
const datas = {
|
||||
category: selectedCategory,
|
||||
SubCategory: subCategoryData?.SubConfigName,
|
||||
key: newIndex, // Push index here
|
||||
};
|
||||
|
||||
return [...prev, datas];
|
||||
});
|
||||
} else {
|
||||
setMessageType('error');
|
||||
setMessageData('Sub Category Already Exist');
|
||||
}
|
||||
ref.current.resetFields(['SubConfigName']);
|
||||
};
|
||||
|
||||
const handleSubmitSubCategory = async () => {
|
||||
if (!tableData || tableData.length === 0) {
|
||||
setMessageType('error');
|
||||
setMessageData('Please add at least one Sub-Category.');
|
||||
return;
|
||||
}
|
||||
|
||||
const subCategoryTypeId = await dispatch(
|
||||
getConfigTypeData({ TypeName: 'Product Sub-Category' })
|
||||
).unwrap();
|
||||
|
||||
const addSubCategoryData = {
|
||||
TypeId: subCategoryTypeId?.data?.data?.[0]?.TypeId || 0,
|
||||
ConfigDetails: tableData.map((item) => ({
|
||||
ConfigName: item.SubCategory,
|
||||
})),
|
||||
AlphaNumFId: AppId,
|
||||
NumFId: selectedCategory,
|
||||
CreatedBy: UserId,
|
||||
};
|
||||
|
||||
const response = await dispatch(
|
||||
postConfigurationmultiple(addSubCategoryData)
|
||||
).unwrap();
|
||||
|
||||
if (response?.data?.statusCode == 1) {
|
||||
setSubCategoryImageUrl('');
|
||||
setMessageType('success');
|
||||
setMessageData('Sub-Category Added Successfully');
|
||||
close(false);
|
||||
setSelectedCategory(null);
|
||||
const subCatResponse = await dispatch(getProdSubCatData({ ConfigId: selectedCategory })).unwrap();
|
||||
setTableData([]);
|
||||
ref?.current?.resetFields();
|
||||
if (products?.length > 0) {
|
||||
const hasTheCategory = products.some(product => product.category === selectedCategory);
|
||||
if (hasTheCategory) {
|
||||
if (subCatResponse?.data?.statusCode == 1) {
|
||||
const subCategories = subCatResponse?.data?.data || [];
|
||||
const availableSubCategories = subCategories?.filter(
|
||||
(subCat) =>
|
||||
subCat.NumFId === parseInt(selectedCategory) && !!subCat?.ConfigName
|
||||
)?.map((subCat) => ({
|
||||
label: subCat.ConfigName,
|
||||
value: subCat.ConfigId,
|
||||
}));
|
||||
|
||||
const updatedProducts = products?.map((product) => {
|
||||
if (product.category === selectedCategory) {
|
||||
return {
|
||||
...product,
|
||||
availableSubCategories,
|
||||
};
|
||||
}
|
||||
return product;
|
||||
});
|
||||
|
||||
setProducts(updatedProducts);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
console.log(products, "selectedCategory", selectedCategory)
|
||||
} else {
|
||||
setMessageType('error');
|
||||
setMessageData(response?.data?.response);
|
||||
ref?.current?.resetFields();
|
||||
}
|
||||
};
|
||||
|
||||
const handleCatDropDownChange = (value) => {
|
||||
setSelectedCategory(value);
|
||||
ref?.current?.setFieldsValue({ CategoryId: value });
|
||||
};
|
||||
|
||||
const statusFormatter = (record, index) => {
|
||||
let filteredData = tableData.filter(
|
||||
(item) => item.SubCategory != record?.SubCategory
|
||||
);
|
||||
setTableData(filteredData);
|
||||
};
|
||||
|
||||
const handleSubCatModalClose = () => {
|
||||
close(false);
|
||||
setSubCategoryImageUrl('');
|
||||
setSelectedCategory(null);
|
||||
setTableData([]);
|
||||
ref?.current?.resetFields();
|
||||
};
|
||||
|
||||
const handleEdit = () => {
|
||||
|
||||
};
|
||||
|
||||
const onComplete = useCallback(() => {
|
||||
setMessageData(null);
|
||||
setMessageType(null);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Messages messageData={messageData} messageType={messageType} onComplete={onComplete} />
|
||||
<DefaultModal
|
||||
title="Add SubCategory"
|
||||
open={open}
|
||||
footer={true}
|
||||
buttonText="Submit"
|
||||
handleSubmit={handleSubmitSubCategory}
|
||||
handleCancel={handleSubCatModalClose}
|
||||
children={
|
||||
<Form ref={ref} className="formDivAnt">
|
||||
<div className="ProSubRowFlex">
|
||||
<div className="subColumnFlex">
|
||||
<div className="subProdCatData">
|
||||
<Form.Item
|
||||
name="CategoryId"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select a Category',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<DropDowns
|
||||
options={categoryData?.map((option) => ({
|
||||
value: option?.ConfigId,
|
||||
label: option?.ConfigName,
|
||||
}))}
|
||||
label={<label class="required">Category</label>}
|
||||
className="field-DropDown"
|
||||
onChangeFunction={handleCatDropDownChange}
|
||||
valueData={selectedCategory}
|
||||
disabled={tableData?.length >= 1}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="subColumnFlex">
|
||||
<div>
|
||||
<Form.Item name="SubConfigName">
|
||||
<InputField
|
||||
label={
|
||||
<label class="required">SubCategory Name</label>
|
||||
}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ fontSize: '25px' }}>
|
||||
<Tooltip title="Add Products" placement="top">
|
||||
<PlusCircleOutlined
|
||||
className="product-add-iconMargin"
|
||||
onClick={async () => {
|
||||
try {
|
||||
const values =
|
||||
await ref.current?.validateFields(
|
||||
['CategoryId', 'SubConfigName']
|
||||
);
|
||||
const trimmedValue = values?.SubConfigName
|
||||
? values.SubConfigName.trim()
|
||||
: '';
|
||||
if (!values?.CategoryId) {
|
||||
throw new Error('Please Select Category');
|
||||
}
|
||||
if (!trimmedValue) {
|
||||
throw new Error('SubCategory Name is required');
|
||||
}
|
||||
await validateSafeInput(trimmedValue);
|
||||
if (trimmedValue.length > 50) {
|
||||
throw new Error(
|
||||
'SubCategory Name should not exceed 50 characters'
|
||||
);
|
||||
}
|
||||
!editDataval ? handleEdit() : handleAddSubCategory();
|
||||
} catch (error) {
|
||||
message.error(
|
||||
error.message || 'Please fill required fields'
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="Quotation-tables"
|
||||
style={{
|
||||
maxwidth: '500px',
|
||||
overflowX: 'auto',
|
||||
scrollbarWidth: 'thin',
|
||||
}}
|
||||
>
|
||||
<Table
|
||||
bordered
|
||||
columns={Columns}
|
||||
dataSource={tableData}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
});
|
||||
|
||||
export default AddSubCategoryModal
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
import { forwardRef, useCallback, useState } from "react";
|
||||
import { DefaultModal } from "../../../Components/Modal/DefaultModal";
|
||||
import { Form } from "antd";
|
||||
import { DropDowns } from "../../../Components/Forms/DropDown";
|
||||
import { InputField } from "../../../Components/Forms/InputField";
|
||||
import { getSession, validateSafeInput } from "../../../Services/Others";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { Messages } from "../../../Components/Notifications/Messages";
|
||||
import { DatePic } from '../../../Components/Forms/DatePicker.jsx';
|
||||
import { getAdmin as getTax } from "../../../Features/Tax/Tax.js";
|
||||
import { getAdmin, postTax } from "../../../Features/ProductPage/ProductPage.js";
|
||||
const AddTaxModal = forwardRef((
|
||||
{
|
||||
open = false,
|
||||
close = () => { },
|
||||
prodTaxData = [],
|
||||
flag = false,
|
||||
}, ref) => {
|
||||
|
||||
const dispatch = useDispatch();
|
||||
console.log(prodTaxData, 'prodTaxData in modal');
|
||||
const AppId = getSession('AppId');
|
||||
const CompId = getSession('CompId');
|
||||
const UserId = getSession('UserId');
|
||||
|
||||
const [messageType, setMessageType] = useState(null);
|
||||
const [messageData, setMessageData] = useState(null);
|
||||
const [selectedTaxNameId, setSelectedTaxNameId] = useState(null);
|
||||
|
||||
const handleTaxNameDropDownChange = async (ConfigId) => {
|
||||
ref.current?.setFieldsValue({ TaxName: ConfigId });
|
||||
await setSelectedTaxNameId(ConfigId);
|
||||
};
|
||||
|
||||
const handleTaxModalClose = () => {
|
||||
close(false);
|
||||
setSelectedTaxNameId(null);
|
||||
ref.current?.resetFields();
|
||||
}
|
||||
|
||||
const handleSubmitTax = async () => {
|
||||
try {
|
||||
const subTaxData = await ref?.current?.validateFields();
|
||||
const effectiveDate = new Date(subTaxData.EffectiveFrom);
|
||||
const formattedDate = effectiveDate.toLocaleDateString('en-CA');
|
||||
const addTaxData = {
|
||||
CompId: CompId,
|
||||
AppId: AppId,
|
||||
TaxName: subTaxData?.TaxName,
|
||||
TaxPercentage: subTaxData?.TaxPercentage,
|
||||
EffectiveFrom: formattedDate,
|
||||
Reference: subTaxData.Reference,
|
||||
CreatedBy: UserId,
|
||||
};
|
||||
|
||||
const response = await dispatch(postTax(addTaxData)).unwrap();
|
||||
if (response?.data?.statusCode == 1) {
|
||||
setMessageType('success');
|
||||
setMessageData(response?.data?.response);
|
||||
close(false);
|
||||
if (flag) {
|
||||
await dispatch(getTax({ CompId: CompId, AppId: AppId }));
|
||||
} else {
|
||||
await dispatch(getAdmin({ CompId: CompId, AppId: AppId }));
|
||||
}
|
||||
ref?.current?.resetFields();
|
||||
setSelectedTaxNameId(null);
|
||||
} else {
|
||||
setMessageType('error');
|
||||
setMessageData(response?.data?.response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error:', error?.message);
|
||||
}
|
||||
};
|
||||
|
||||
const onComplete = useCallback(() => {
|
||||
setMessageType(null);
|
||||
setMessageData(null);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Messages messageData={messageData} messageType={messageType} onComplete={onComplete} />
|
||||
<DefaultModal
|
||||
title="Add Tax"
|
||||
open={open}
|
||||
footer={true}
|
||||
buttonText="Submit"
|
||||
children={
|
||||
<Form ref={ref} className="formDivAnt">
|
||||
<div className="ProSubRowFlex">
|
||||
<Form.Item name="TaxName">
|
||||
<DropDowns
|
||||
options={prodTaxData?.map((option) => ({
|
||||
value: option.ConfigId,
|
||||
label: option.ConfigName,
|
||||
}))}
|
||||
label="Tax Name"
|
||||
className="field-DropDown"
|
||||
onChangeFunction={handleTaxNameDropDownChange}
|
||||
valueData={selectedTaxNameId}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="TaxPercentage"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
pattern: /^(?:100(?:\.0+)?|\d{1,2}(?:\.\d+)?)$/,
|
||||
message: 'Please Enter Valid Percentage',
|
||||
},
|
||||
{
|
||||
validator: async (_, value) => {
|
||||
await validateSafeInput(value); // To block HTML tags & SQL keywords
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputField
|
||||
autoComplete="off"
|
||||
label="Tax Percentage"
|
||||
inputMode="decimal"
|
||||
onInput={(e) => {
|
||||
let cleanedValue = e.target.value.replace(
|
||||
/[^0-9.]/g,
|
||||
''
|
||||
); // Remove invalid characters
|
||||
const parts = cleanedValue.split('.');
|
||||
|
||||
if (cleanedValue.startsWith('.')) {
|
||||
cleanedValue = '0' + cleanedValue;
|
||||
}
|
||||
|
||||
e.target.value =
|
||||
parts.length > 2
|
||||
? `${parts[0]}.${parts.slice(1).join('')}`
|
||||
: cleanedValue;
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="Reference"
|
||||
rules={[
|
||||
{
|
||||
pattern: /^(?!\s*$).+/,
|
||||
message: 'Please Enter Reference',
|
||||
},
|
||||
{
|
||||
validator: async (_, value) => {
|
||||
await validateSafeInput(value); // To block HTML tags & SQL keywords
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputField label="Reference No" autoComplete="off" />
|
||||
</Form.Item>
|
||||
<div>
|
||||
<label className="required">Effective From :</label>
|
||||
</div>
|
||||
|
||||
<Form.Item
|
||||
name="EffectiveFrom"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please Enter Effective From',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<DatePic
|
||||
field="EffectiveFrom"
|
||||
type="number"
|
||||
min={1}
|
||||
max={100}
|
||||
fieldState={true}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Form>
|
||||
}
|
||||
handleSubmit={handleSubmitTax}
|
||||
handleCancel={handleTaxModalClose}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
});
|
||||
|
||||
export default AddTaxModal;
|
||||
|
|
@ -2,12 +2,19 @@ import { memo, useState, useEffect } from "react";
|
|||
import { DropDowns } from "../../../Components/Forms/DropDown";
|
||||
import { Checkbox, Popover } from "antd";
|
||||
import { Settings, X } from "lucide-react";
|
||||
import { PlusCircleOutlined } from '@ant-design/icons';
|
||||
import { Tooltip } from 'antd';
|
||||
import { getActiveProdSubCatData } from "../../../Features/ProductPage/ProductPage";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { getSession } from "../../../Services/Others";
|
||||
|
||||
const DefaultSettingPopover = memo(({ field, options, onSubmit, currentDefault, popOverHeading, checkBoxHeading = 'Apply to all existing rows', showField }) => {
|
||||
const DefaultSettingPopover = memo(({ field, options, onSubmit, currentDefault, popOverHeading, checkBoxHeading = 'Apply to all existing rows', showField, Component = null, triggerModal = () => { }, needAllSubCategories = false }) => {
|
||||
const dispatch = useDispatch();
|
||||
const [selectedValue, setSelectedValue] = useState(currentDefault);
|
||||
const [applyToAll, setApplyToAll] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
console.log(showField, "showField")
|
||||
const [fetchSubCategoriesData, setFetchSubCategoriesData] = useState(false);
|
||||
console.log(Component, "showField")
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setSelectedValue(currentDefault);
|
||||
|
|
@ -15,6 +22,17 @@ const DefaultSettingPopover = memo(({ field, options, onSubmit, currentDefault,
|
|||
}
|
||||
}, [open, currentDefault]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const fetchAllSubCategories = async () => {
|
||||
await (dispatch(getActiveProdSubCatData({ AppId: getSession('AppId') })))?.unwrap();
|
||||
}
|
||||
|
||||
if (fetchSubCategoriesData) {
|
||||
fetchAllSubCategories();
|
||||
}
|
||||
}, [fetchSubCategoriesData]);
|
||||
|
||||
const handleSubmit = () => {
|
||||
onSubmit(field, selectedValue, applyToAll);
|
||||
setOpen(false);
|
||||
|
|
@ -45,15 +63,28 @@ const DefaultSettingPopover = memo(({ field, options, onSubmit, currentDefault,
|
|||
}}
|
||||
>{showField}</div>}
|
||||
|
||||
<div className="popover-section">
|
||||
<div className="popover-section" style={{ display: 'flex', alignItems: 'flex-start', gap: '5px' }}>
|
||||
<DropDowns
|
||||
label={`Select default ${getFieldTitle(field).toLowerCase()}`}
|
||||
valueData={selectedValue}
|
||||
onChangeFunction={setSelectedValue}
|
||||
options={options}
|
||||
/>
|
||||
{Component && (
|
||||
<Tooltip title={`Add ${getFieldTitle(field)}`} placement="top">
|
||||
<PlusCircleOutlined
|
||||
className="product-add-iconMargin"
|
||||
onClick={() => {
|
||||
triggerModal(true);
|
||||
if (needAllSubCategories) {
|
||||
setFetchSubCategoriesData(true);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{Component}
|
||||
<div className="popover-checkbox">
|
||||
<Checkbox
|
||||
checked={applyToAll}
|
||||
|
|
@ -76,6 +107,7 @@ const DefaultSettingPopover = memo(({ field, options, onSubmit, currentDefault,
|
|||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Popover
|
||||
content={content}
|
||||
trigger="click"
|
||||
|
|
@ -86,6 +118,7 @@ const DefaultSettingPopover = memo(({ field, options, onSubmit, currentDefault,
|
|||
>
|
||||
<Settings size={14} className="setting-icon" />
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue