import { useCallback, useEffect, useState } from 'react'; import { Input, InputNumber, Select, Radio, Button, message } from 'antd'; import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'; import './MembershipForm.scss'; import { useDispatch } from 'react-redux'; import { getSession } from '../../Services/Others'; import { changeBreadCrumb } from '../../Features/AppPage/CenterPage.js'; import { Messages } from '../../Components/Notifications/Messages.jsx'; import { postMembership, putMembership, } from '../../Features/Membership/Membership.js'; import { getAdmin } from '../../Features/ProductPage/ProductPage.js'; import { useLocation, useNavigate } from 'react-router-dom'; const subDirectory = import.meta.env.ENV_BASE_URL; const { Option } = Select; const DAYS_OF_WEEK = [ { label: 'Monday', value: 'Monday' }, { label: 'Tuesday', value: 'Tuesday' }, { label: 'Wednesday', value: 'Wednesday' }, { label: 'Thursday', value: 'Thursday' }, { label: 'Friday', value: 'Friday' }, { label: 'Saturday', value: 'Saturday' }, { label: 'Sunday', value: 'Sunday' }, ]; const MembershipForm = ({ formType = 'add' }) => { const dispatch = useDispatch(); const navigate = useNavigate(); const location = useLocation(); const state = location?.state; const editstate = state?.editstate; console.log(editstate, 'editstate'); const AppId = getSession('AppId'); const CompId = getSession('CompId'); const BranchId = getSession('BranchId'); const UserId = getSession('UserId'); const [messageType, setMessageType] = useState(null); const [messageData, setMessageData] = useState(null); const [taxData, setTaxData] = useState([]); const [membershipName, setMembershipName] = useState(''); const [nameError, setNameError] = useState(''); const [schedules, setSchedules] = useState([ { id: Date.now(), days: null, price: null, displayPrice: null, taxId: null, hrs: null, discountType: 'F', discountValue: null, priority: 'N', selectedDays: [], remarks: '', membershipId: null, uniqueId: null, planId: null, activeStatus: null, errors: {}, }, ]); const items = [ { name: 'Home', link: `${subDirectory}app-page/home`, }, { name: 'Membership List', link: `${subDirectory}setting/membership-master/list`, }, { name: editstate ? 'Edit' : 'New', link: null, }, ]; console.log(taxData, 'taxData'); useEffect(() => { getTaxData(); dispatch(changeBreadCrumb({ items: items })); if (editstate) { // Populate membership name setMembershipName(editstate?.MembershipName || ''); // Populate schedules from MembershipPlanDtl if ( editstate?.MembershipPlanDtl && editstate.MembershipPlanDtl.length > 0 ) { const populatedSchedules = editstate.MembershipPlanDtl.map((plan) => ({ id: plan.UniqueId || Date.now(), days: plan.PlanDuration, price: plan.Price, displayPrice: plan.DisplayPrice, taxId: plan.TaxId, hrs: plan.FreeSessionHrs, discountType: plan.DiscountType, discountValue: plan.Discount, priority: plan.Priority, selectedDays: plan.Prioritydays?.[0]?.split(',')?.map((day) => day.trim()) || [], remarks: plan.Remarks || '', membershipId: plan.MembershipId, uniqueId: plan.UniqueId, planId: plan.PlanId, activeStatus: plan.ActiveStatus, errors: {}, })); setSchedules(populatedSchedules); } } }, []); const getTaxData = async () => { const res = await dispatch( getAdmin({ CompId: CompId, AppId: AppId }) ).unwrap(); if (res?.data?.statusCode === 1) { setTaxData(res?.data?.data); } else { setTaxData([]); } }; const validateMembershipName = (value) => { if (!value || value.trim() === '') { setNameError('Membership name is required'); return false; } setNameError(''); return true; }; const handleScheduleChange = (id, field, value) => { const index = schedules.findIndex((schedule) => schedule.id === id); const newSchedules = [...schedules]; newSchedules[index] = { ...newSchedules[index], [field]: value }; // ✅ Clear hrs when days change if (field === 'days') { newSchedules[index].hrs = null; } // ✅ Reset selectedDays if priority is N if (field === 'priority' && value === 'N') { newSchedules[index].selectedDays = []; } // Only validate the current field being changed const currentErrors = { ...newSchedules[index].errors }; const fieldValidation = validateSingleField( newSchedules[index], field, newSchedules ); if (fieldValidation) { currentErrors[field] = fieldValidation; } else { delete currentErrors[field]; } newSchedules[index].errors = currentErrors; setSchedules(newSchedules); }; const validateSingleField = (schedule, field, allSchedules = schedules) => { switch (field) { case 'days': if (!schedule.days) return 'Days is required'; break; case 'price': if (!schedule.price) return 'Price is required'; break; case 'displayPrice': if (!schedule.displayPrice) return 'Display price is required'; if ( schedule.displayPrice && schedule.price && Number(schedule.displayPrice) > Number(schedule.price) ) { return 'Display price cannot be greater than price'; } break; case 'taxId': if (!schedule.taxId) return 'Tax is required'; break; case 'hrs': if (!schedule.hrs) return 'Hours is required'; if ( schedule.days && schedule.hrs && schedule.hrs > schedule.days * 24 ) { return `Hours cannot exceed ${schedule.days * 24}`; } break; case 'discountValue': if (schedule.discountValue === null || schedule.discountValue === '') return 'Discount is required'; if (schedule.discountType === 'P' && schedule.discountValue > 99) { return 'Percentage discount cannot exceed 99%'; } if ( schedule.discountType === 'F' && schedule.discountValue >= schedule.displayPrice ) { return 'Fixed discount cannot be greater than or equal to display price'; } break; case 'selectedDays': if (schedule.priority === 'Y' && schedule.selectedDays.length === 0) { return 'Please select at least one day'; } break; } return null; }; const validateSchedule = (schedule, index, allSchedules = schedules) => { const errors = {}; // ✅ Required fields if (!schedule.days) errors.days = 'Days is required'; if (!schedule.price) errors.price = 'Price is required'; if (!schedule.displayPrice) errors.displayPrice = 'Display price is required'; if (!schedule.taxId) errors.taxId = 'Tax is required'; if (!schedule.hrs) errors.hrs = 'Hours is required'; if (schedule.discountValue === null || schedule.discountValue === '') errors.discountValue = 'Discount is required'; if (schedule.priority === 'Y' && schedule.selectedDays.length === 0) errors.selectedDays = 'Please select at least one day'; // ✅ Display price <= price if ( schedule.displayPrice && schedule.price && Number(schedule.displayPrice) > Number(schedule.price) ) { errors.displayPrice = 'Display price cannot be greater than price'; } // ✅ Hours ≤ days * 24 if (schedule.days && schedule.hrs && schedule.hrs > schedule.days * 24) { errors.hrs = `Hours cannot exceed ${schedule.days * 24}`; } // ✅ Discount validation if (schedule.discountType === 'P' && schedule.discountValue > 99) { errors.discountValue = 'Percentage discount cannot exceed 99%'; } if ( schedule.discountType === 'F' && schedule.discountValue >= schedule.displayPrice ) { errors.discountValue = 'Fixed discount cannot be greater than or equal to display price'; } // ✅ Duplicate Check (uses current updated allSchedules) allSchedules.forEach((other, idx) => { if (idx === index) return; // skip self const baseMatch = Number(other.days) === Number(schedule.days) && Number(other.price) === Number(schedule.price) && Number(other.displayPrice) === Number(schedule.displayPrice) && String(other.taxId) === String(schedule.taxId) && Number(other.hrs) === Number(schedule.hrs) && String(other.discountType) === String(schedule.discountType) && Number(other.discountValue) === Number(schedule.discountValue) && String(other.priority) === String(schedule.priority); if (baseMatch) { // both are non-priority plans if (schedule.priority === 'N' && other.priority === 'N') { errors.duplicate = 'Plan already exists'; } // both are priority plans -> check selectedDays array match else if (schedule.priority === 'Y' && other.priority === 'Y') { const daysMatch = schedule.selectedDays.length === other.selectedDays.length && schedule.selectedDays.every((d) => other.selectedDays.includes(d)); if (daysMatch) { errors.duplicate = 'Plan already exists (same priority days)'; } } } }); return errors; }; const addSchedule = () => { if (!validateMembershipName(membershipName)) { message.error('Please enter membership name first'); return; } const validatedSchedules = schedules.map((schedule, index) => ({ ...schedule, errors: validateSchedule(schedule, index, schedules), })); setSchedules(validatedSchedules); const hasErrors = validatedSchedules.some( (schedule) => Object.keys(schedule.errors).length > 0 ); if (hasErrors) { message.error('Please fix all errors before adding a another plan'); return; } const membershipId = (editstate && editstate.MembershipId) ? editstate.MembershipId : null; setSchedules([ ...schedules, { id: Date.now(), days: null, price: null, displayPrice: null, taxId: null, hrs: null, discountType: 'F', discountValue: null, priority: 'N', selectedDays: [], remarks: '', membershipId: membershipId, uniqueId: null, planId: null, activeStatus: null, errors: {}, }, ]); }; const removeSchedule = (id) => { if (schedules?.filter(schedule => schedule?.activeStatus !== 'D').length === 1) { message.warning('At least one plan is required'); return; } const scheduleToRemove = schedules.find((sch) => sch.id === id); const index = schedules.findIndex((sch) => sch.id === id); if (scheduleToRemove.planId) { // If planId exists, set activeStatus to 'D' const newSchedules = [...schedules]; newSchedules[index] = { ...newSchedules[index], activeStatus: 'D' }; setSchedules(newSchedules); } else { // If no planId, filter out the record const newSchedules = schedules.filter((_, i) => i !== index); setSchedules(newSchedules); } }; const handleSubmit = async () => { if (!validateMembershipName(membershipName)) { message.error('Please enter membership name'); return; } // Validate all schedules and set errors const validatedSchedules = schedules.map((schedule, index) => ({ ...schedule, errors: validateSchedule(schedule, index, schedules), })); setSchedules(validatedSchedules); const hasErrors = validatedSchedules.some( (schedule) => Object.keys(schedule.errors).length > 0 ); if (hasErrors) { message.error('Please fix all errors before submitting'); return; } const formData = { AppId: AppId, CompId: CompId, BranchId: BranchId, MembershipName: membershipName, MembershipPlanDtl: schedules.map((schedule) => ({ PlanDuration: schedule.days, DisplayPrice: schedule.displayPrice, Price: schedule.price, TaxId: schedule.taxId || null, FreeSessionHrs: schedule.hrs, Discount: schedule.discountValue, DiscountType: schedule.discountType, Priority: schedule.priority, Prioritydays: schedule.selectedDays, Remarks: schedule.remarks || '', UniqueId: schedule.uniqueId || null, PlanId: schedule.planId || null, MembershipId: schedule.membershipId || null, ActiveStatus: schedule.activeStatus || null, TaxAmount: calculateTaxAmount(schedule.displayPrice, schedule.taxId), })), }; // Add edit-specific fields if in edit mode let res = {}; if (editstate) { formData.MembershipId = editstate.MembershipId; formData.UpdatedBy = UserId; formData.ActiveStatus = editstate.ActiveStatus; formData.UniqueId = editstate.UniqueId; res = await dispatch(putMembership(formData)).unwrap(); } else { formData.CreatedBy = UserId; res = await dispatch(postMembership(formData)).unwrap(); } if (res?.data?.statusCode === 1) { navigate(`${subDirectory}setting/membership-master/list`, { state: { Notify: { messageType: 'success', messageData: res?.data?.response, }, }, }); } else { setMessageData(res?.data?.response); setMessageType('error'); } console.log('Form Data:', formData); }; const calculateTaxAmount = (price, taxId) => { const taxRate = taxData.find((tax) => tax.TaxId === taxId)?.TaxPercentage; return price * (taxRate / 100); }; const onComplete = useCallback(() => { setMessageData(null); setMessageType(null); }, []); return (

{editstate ? 'Edit Membership' : 'Create Membership'}

{ setMembershipName(e.target.value); validateMembershipName(e.target.value); }} onBlur={(e) => validateMembershipName(e.target.value)} status={nameError ? 'error' : ''} className="membershipFormInput" size="large" /> {nameError && (
{nameError}
)}
{schedules?.filter(schedule => schedule?.activeStatus !== 'D').map((schedule, index) => (

Plan {index + 1}

{schedules?.filter(schedule => schedule?.activeStatus !== 'D').length > 1 && ( )}
handleScheduleChange(schedule.id, 'days', value?.target?.value) } status={schedule.errors.days ? 'error' : ''} className="membershipFormInput" size="large" style={{ width: '100%' }} inputMode="decimal" onInput={(e) => { const cleanedValue = e.target.value.replace( /[^0-9.]/g, '' ); const parts = cleanedValue.split('.'); e.target.value = parts.length > 2 ? `${parts[0]}.${parts.slice(1).join('')}` : cleanedValue; }} /> {schedule.errors.days && (
{schedule.errors.days}
)}
handleScheduleChange(schedule.id, 'price', value?.target?.value) } // min={0} // prefix="₹" status={schedule.errors.price ? 'error' : ''} className="membershipFormInput" size="large" style={{ width: '100%' }} inputMode="decimal" onInput={(e) => { const cleanedValue = e.target.value.replace( /[^0-9.]/g, '' ); const parts = cleanedValue.split('.'); e.target.value = parts.length > 2 ? `${parts[0]}.${parts.slice(1).join('')}` : cleanedValue; }} /> {schedule.errors.price && (
{schedule.errors.price}
)}
handleScheduleChange( schedule.id, 'displayPrice', value?.target?.value ) } // min={0} // prefix="₹" status={schedule.errors.displayPrice ? 'error' : ''} className="membershipFormInput" size="large" style={{ width: '100%' }} inputMode="decimal" onInput={(e) => { const cleanedValue = e.target.value.replace( /[^0-9.]/g, '' ); const parts = cleanedValue.split('.'); e.target.value = parts.length > 2 ? `${parts[0]}.${parts.slice(1).join('')}` : cleanedValue; }} /> {schedule.errors.displayPrice && (
{schedule.errors.displayPrice}
)}
{schedule.errors.taxId && (
{schedule.errors.taxId}
)}
handleScheduleChange(schedule.id, 'hrs', value?.target?.value) } // min={0} status={schedule.errors.hrs ? 'error' : ''} className="membershipFormInput" size="large" style={{ width: '100%' }} inputMode="decimal" onInput={(e) => { const cleanedValue = e.target.value.replace( /[^0-9.]/g, '' ); const parts = cleanedValue.split('.'); e.target.value = parts.length > 2 ? `${parts[0]}.${parts.slice(1).join('')}` : cleanedValue; }} /> {schedule.errors.hrs && (
{schedule.errors.hrs}
)}
handleScheduleChange(schedule.id, 'discountValue', value) } min={0} prefix={schedule.discountType === 'F' ? '₹' : ''} suffix={schedule.discountType === 'P' ? '%' : ''} max={schedule.discountType === 'P' ? 100 : undefined} status={schedule.errors.discountValue ? 'error' : ''} className="membershipFormInput" size="large" style={{ width: '100%' }} /> {schedule.errors.discountValue && (
{schedule.errors.discountValue}
)}
handleScheduleChange(schedule.id, 'priority', e.target.value) } size="large" > Yes No
{schedule.priority === 'Y' && (
{schedule.errors.selectedDays && (
{schedule.errors.selectedDays}
)}
)}
handleScheduleChange(schedule.id, 'remarks', e.target.value) } rows={3} // Adjust the number of rows as needed className="membershipFormInputTextArea" size="large" />
{schedule.errors.duplicate && (
{schedule.errors.duplicate}
)}
))}
); }; export default MembershipForm;