Remove and add more button included CREATE PAGE
Mon May 26 2025 18:39:09 GMT+0000 (Coordinated Universal Time)
Saved by @krisha_joshi
import React, { useState, useCallback, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import { McButton, McInput, McMultiSelect, McSelect } from '@maersk-global/mds-react-wrapper'; import { McOption } from '@maersk-global/mds-react-wrapper/components-core/mc-option'; import styles from '../styles/CreateRule.module.css'; import data from '../data/PnLGroup.json'; class ErrorBoundary extends React.Component { state = { hasError: false }; static getDerivedStateFromError() { return { hasError: true }; } render() { if (this.state.hasError) { return <div>Something went wrong. Please refresh the page.</div>; } return this.props.children; } } const CreateRules = () => { const navigate = useNavigate(); const [activeTab, setActiveTab] = useState('ruleInfo'); const [isLoading, setIsLoading] = useState(false); const [ruleData, setRuleData] = useState({ num: '', name: '', desc: '', custRefID: '', ruleGroup: '', isActive: 'Y', pnlGroup: '', }); const [steps, setSteps] = useState([ { stepNo: '', stepName: 'Single Step', stepDesc: '', stepType: 'S', preAggregatorColumns: [], sourceTableID: '', sourceFilterSets: [{ filters: [], operator: '', values: [] }], joinColumns: [], allocationColumns: [], driverTableID: '', driverWeightColumn: '', driverFilterSets: [{ filters: [], operator: '', values: [] }], }, ]); const [errors, setErrors] = useState({ rule: {}, steps: [{ sourceFilterSets: [{}], driverFilterSets: [{}] }] }); const pnLGroups = data.PnLGroups && typeof data.PnLGroups === 'object' ? Object.keys(data.PnLGroups) : []; const ruleGroups = ruleData.pnlGroup && data.PnLGroups[ruleData.pnlGroup] ? data.PnLGroups[ruleData.pnlGroup].RuleGroups || [] : []; const sourceFilterOptions = [ { value: 'Source_Filter_1', label: 'Source Filter 1' }, { value: 'Source_Filter_2', label: 'Source Filter 2' }, { value: 'Source_Filter_3', label: 'Source Filter 3' }, ]; const sourceValueOptions = [ { value: 'Source_Value_1', label: 'Source Value 1' }, { value: 'Source_Value_2', label: 'Source Value 2' }, { value: 'Source_Value_3', label: 'Source Value 3' }, ]; const preAggregatorOptions = [ { value: 'column1', label: 'Column 1' }, { value: 'column2', label: 'Column 2' }, { value: 'column3', label: 'Column 3' }, ]; const joinColumnsOptions = [ { value: 'join_col1', label: 'Join Column 1' }, { value: 'join_col2', label: 'Join Column 2' }, { value: 'join_col3', label: 'Join Column 3' }, ]; const allocationColumnsOptions = [ { value: 'alloc_col1', label: 'Allocation Column 1' }, { value: 'alloc_col2', label: 'Allocation Column 2' }, { value: 'alloc_col3', label: 'Allocation Column 3' }, ]; const driverFilterOptions = [ { value: 'Driver_Type_1', label: 'Driver Type: Type 1' }, { value: 'Driver_Type_2', label: 'Driver Type: Type 2' }, { value: 'Driver_Status_Active', label: 'Driver Status: Active' }, ]; const driverValueOptions = [ { value: 'Driver_Value_1', label: 'Driver Value 1' }, { value: 'Driver_Value_2', label: 'Driver Value 2' }, { value: 'Driver_Value_3', label: 'Driver Value 3' }, ]; const operatorOptions = useMemo(() => [ { value: 'IN', label: 'IN' }, { value: 'NOT IN', label: 'NOT IN' }, { value: 'EQ', label: 'EQ' }, { value: 'NTEQ', label: 'NTEQ' }, { value: 'IS NULL', label: 'IS NULL' }, { value: 'GT', label: 'GT' }, { value: 'LT', label: 'LT' }, { value: 'GTEQ', label: 'GTEQ' }, { value: 'LTEQ', label: 'LTEQ' }, { value: 'BETWEEN', label: 'BETWEEN' }, { value: 'NOT BETWEEN', label: 'NOT BETWEEN' }, { value: 'LIKE', label: 'LIKE' }, ], []); const addStep = useCallback(() => { setSteps((prevSteps) => [ ...prevSteps, { stepNo: '', stepName: 'Single Step', stepDesc: '', stepType: 'S', preAggregatorColumns: [], sourceTableID: '', sourceFilterSets: [{ filters: [], operator: '', values: [] }], joinColumns: [], allocationColumns: [], driverTableID: '', driverWeightColumn: '', driverFilterSets: [{ filters: [], operator: '', values: [] }], }, ]); setErrors((prevErrors) => ({ ...prevErrors, steps: [...prevErrors.steps, { sourceFilterSets: [{}], driverFilterSets: [{}] }], })); }, []); const removeStep = useCallback((index) => { if (steps.length === 1) { alert('At least one step is required.'); return; } setSteps((prevSteps) => prevSteps.filter((_, i) => i !== index)); setErrors((prevErrors) => ({ ...prevErrors, steps: prevErrors.steps.filter((_, i) => i !== index), })); }, [steps.length]); const addFilterSet = useCallback((stepIndex, type) => { setSteps((prevSteps) => { const newSteps = [...prevSteps]; const filterKey = type === 'source' ? 'sourceFilterSets' : 'driverFilterSets'; newSteps[stepIndex] = { ...newSteps[stepIndex], [filterKey]: [...newSteps[stepIndex][filterKey], { filters: [], operator: '', values: [] }], }; return newSteps; }); setErrors((prevErrors) => { const newStepsErrors = [...prevErrors.steps]; const filterErrorsKey = type === 'source' ? 'sourceFilterSets' : 'driverFilterSets'; newStepsErrors[stepIndex] = { ...newStepsErrors[stepIndex], [filterErrorsKey]: [...newStepsErrors[stepIndex][filterErrorsKey], {}], }; return { ...prevErrors, steps: newStepsErrors }; }); }, []); const removeFilterSet = useCallback((stepIndex, filterIndex, type) => { setSteps((prevSteps) => { const newSteps = [...prevSteps]; const filterKey = type === 'source' ? 'sourceFilterSets' : 'driverFilterSets'; newSteps[stepIndex] = { ...newSteps[stepIndex], [filterKey]: newSteps[stepIndex][filterKey].filter((_, i) => i !== filterIndex), }; return newSteps; }); setErrors((prevErrors) => { const newStepsErrors = [...prevErrors.steps]; const filterErrorsKey = type === 'source' ? 'sourceFilterSets' : 'driverFilterSets'; newStepsErrors[stepIndex] = { ...newStepsErrors[stepIndex], [filterErrorsKey]: newStepsErrors[stepIndex][filterErrorsKey].filter((_, i) => i !== filterIndex), }; return { ...prevErrors, steps: newStepsErrors }; }); }, []); const validateForm = useCallback(() => { try { const newErrors = { rule: {}, steps: steps.map(() => ({ sourceFilterSets: [], driverFilterSets: [] })) }; let isValid = true; if (!ruleData.num) { newErrors.rule.num = 'Rule Number is required'; isValid = false; } else if (!/^[a-zA-Z0-9]+$/.test(ruleData.num)) { newErrors.rule.num = 'Rule Number must be alphanumeric'; isValid = false; } if (!ruleData.name) { newErrors.rule.name = 'Rule Name is required'; isValid = false; } if (!ruleData.desc) { newErrors.rule.desc = 'Description is required'; isValid = false; } if (!ruleData.custRefID) { newErrors.rule.custRefID = 'Customer Reference ID is required'; isValid = false; } if (!ruleData.pnlGroup) { newErrors.rule.pnlGroup = 'PnL Group is required'; isValid = false; } if (!ruleData.ruleGroup) { newErrors.ruleGroup = 'Rule Group is required'; isValid = false; } if (!ruleData.isActive) { newErrors.rule.isActive = 'Active status is required'; isValid = false; } const stepNumbers = new Set(); steps.forEach((step, index) => { const stepErrors = { sourceFilterSets: step.sourceFilterSets.map(() => ({})), driverFilterSets: step.driverFilterSets.map(() => ({})) }; if (!step.stepNo) { stepErrors.stepNo = 'Step Number is required'; isValid = false; } else if (stepNumbers.has(step.stepNo)) { stepErrors.stepNo = 'Step Number must be unique'; isValid = false; } else { stepNumbers.add(step.stepNo); } if (!step.stepName) { stepErrors.stepName = 'Step Name is required'; isValid = false; } if (!step.stepDesc) { stepErrors.stepDesc = 'Step Description is required'; isValid = false; } if (!step.stepType) { stepErrors.stepType = 'Step Type is required'; isValid = false; } if (!step.preAggregatorColumns.length) { stepErrors.preAggregatorColumns = 'Pre-Aggregator Columns is required'; isValid = false; } if (!step.sourceTableID) { stepErrors.sourceTableID = 'Source Table ID is required'; isValid = false; } if (!step.sourceFilterSets.some(set => set.filters.length && set.operator && set.values.length)) { stepErrors.sourceFilterSets[0].filters = 'At least one complete Source Filter set is required'; isValid = false; } step.sourceFilterSets.forEach((set, filterIndex) => { if (set.filters.length || set.operator || set.values.length) { if (!set.filters.length) { stepErrors.sourceFilterSets[filterIndex].filters = 'Source Filters is required'; isValid = false; } if (!set.operator) { stepErrors.sourceFilterSets[filterIndex].operator = 'Source Operator is required'; isValid = false; } if (!set.values.length) { stepErrors.sourceFilterSets[filterIndex].values = 'Source Values is required'; isValid = false; } } }); if (!step.joinColumns.length) { stepErrors.joinColumns = 'Join Columns is required'; isValid = false; } if (!step.allocationColumns.length) { stepErrors.allocationColumns = 'Allocation Columns is required'; isValid = false; } if (!step.driverTableID) { stepErrors.driverTableID = 'Driver Table ID is required'; isValid = false; } if (!step.driverWeightColumn) { stepErrors.driverWeightColumn = 'Driver Weight Column is required'; isValid = false; } if (!step.driverFilterSets.some(set => set.filters.length && set.operator && set.values.length)) { stepErrors.driverFilterSets[0].filters = 'At least one complete Driver Filter set is required'; isValid = false; } step.driverFilterSets.forEach((set, filterIndex) => { if (set.filters.length || set.operator || set.values.length) { if (!set.filters.length) { stepErrors.driverFilterSets[filterIndex].filters = 'Driver Filters is required'; isValid = false; } if (!set.operator) { stepErrors.driverFilterSets[filterIndex].operator = 'Driver Operator is required'; isValid = false; } if (!set.values.length) { stepErrors.driverFilterSets[filterIndex].values = 'Driver Values is required'; isValid = false; } } }); newErrors.steps[index] = stepErrors; }); setErrors(newErrors); return isValid; } catch (error) { alert('An error occurred during form validation. Please try again.'); return false; } }, [ruleData, steps]); const parseColumns = (input) => input; const parseFilters = (filterSets) => { return filterSets .filter(set => set.filters.length && set.operator && set.values.length) .map(set => ({ name: set.filters, filterType: set.operator, values: set.values, })); }; const handleInputChange = useCallback((e, stepIndex = null) => { const { name, value } = e.target; if (stepIndex !== null) { setSteps((prevSteps) => { const newSteps = [...prevSteps]; newSteps[stepIndex] = { ...newSteps[stepIndex], [name]: value }; return newSteps; }); setErrors((prevErrors) => { const newStepsErrors = [...prevErrors.steps]; newStepsErrors[stepIndex] = { ...newStepsErrors[stepIndex], [name]: '', }; return { ...prevErrors, steps: newStepsErrors }; }); } else { setRuleData((prevData) => ({ ...prevData, [name]: value, ...(name === 'pnlGroup' ? { ruleGroup: '' } : {}), })); setErrors((prevErrors) => ({ ...prevErrors, rule: { ...prevErrors.rule, [name]: '' }, })); } }, []); const handleFilterSetChange = useCallback((e, stepIndex, filterIndex, type, field) => { const filterKey = type === 'source' ? 'sourceFilterSets' : 'driverFilterSets'; const value = field === 'operator' ? e.target.value : e.detail.map(option => option.value); setSteps((prevSteps) => { const newSteps = [...prevSteps]; newSteps[stepIndex] = { ...newSteps[stepIndex], [filterKey]: newSteps[stepIndex][filterKey].map((set, i) => i === filterIndex ? { ...set, [field]: value } : set ), }; return newSteps; }); setErrors((prevErrors) => { const newStepsErrors = [...prevErrors.steps]; const filterErrorsKey = type === 'source' ? 'sourceFilterSets' : 'driverFilterSets'; newStepsErrors[stepIndex] = { ...newStepsErrors[stepIndex], [filterErrorsKey]: newStepsErrors[stepIndex][filterErrorsKey].map((setErrors, i) => i === filterIndex ? { ...setErrors, [field]: '' } : setErrors ), }; return { ...prevErrors, steps: newStepsErrors }; }); }, []); const resetForm = useCallback(() => { setRuleData({ num: '', name: '', desc: '', custRefID: '', ruleGroup: '', isActive: 'Y', pnlGroup: '', }); setSteps([ { stepNo: '', stepName: 'Single Step', stepDesc: '', stepType: 'S', preAggregatorColumns: [], sourceTableID: '', sourceFilterSets: [{ filters: [], operator: '', values: [] }], joinColumns: [], allocationColumns: [], driverTableID: '', driverWeightColumn: '', driverFilterSets: [{ filters: [], operator: '', values: [] }], }, ]); setErrors({ rule: {}, steps: [{ sourceFilterSets: [{}], driverFilterSets: [{}] }] }); setActiveTab('ruleInfo'); }, []); const handleSave = useCallback(async () => { if (!validateForm()) { alert('Please fill out all required fields.'); return; } setIsLoading(true); const ruleJson = { rules: { rule: [ { num: ruleData.num, name: ruleData.name, desc: ruleData.desc, custRefID: ruleData.custRefID, ruleGroup: ruleData.ruleGroup, isActive: ruleData.isActive, Step: steps.map((step, index) => ({ stepNo: step.stepNo || `${ruleData.num}.${index + 1}`, stepName: step.stepName === 'Single Step' ? 'single' : 'multi', stepDesc: step.stepDesc, stepType: step.stepType, isActive: 'Y', SourceTable: { id: step.sourceTableID, Name: step.sourceTableID, }, sourceFilters: { columns: parseFilters(step.sourceFilterSets), }, preAggregator: { columns: parseColumns(step.preAggregatorColumns), }, join: { columns: parseColumns(step.joinColumns), }, allocation: { columns: parseColumns(step.allocationColumns), }, driver: { driverTableID: step.driverTableID, driverWeightColumn: step.driverWeightColumn, driverFilters: { columns: parseFilters(step.driverFilterSets), }, }, })), }, ], }, }; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 10000); try { const response = await fetch('/api/rules', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify(ruleJson), signal: controller.signal, }); clearTimeout(timeoutId); if (response.ok) { alert('Rule created successfully!'); resetForm(); navigate('/'); } else { const errorData = await response.json().catch(() => ({ message: response.statusText })); alert(`Failed to create rule: ${errorData.message || response.statusText}`); } } catch (error) { if (error.name === 'AbortError') { alert('Request timed out. Please try again.'); } else { alert('An error occurred while saving the rule. Please try again.'); } } finally { setIsLoading(false); } }, [validateForm, ruleData, steps, resetForm, navigate]); const handleCancel = useCallback(() => { navigate('/'); }, [navigate]); const renderTabContent = () => { switch (activeTab) { case 'ruleInfo': return ( <div className={styles.tabContent}> {Object.values(errors.rule).some((error) => error) && ( <div className={styles.errorSummary}> <h4>Please fix the following errors:</h4> <ul> {Object.entries(errors.rule).map(([key, error]) => error && ( <li key={key}>{error}</li> ))} </ul> </div> )} <h3 className={styles.sectionTitle}>Rule Information</h3> <div className={styles.formGrid}> <div className={styles.gridItem}> <McSelect label="PnL Group" name="pnlGroup" value={ruleData.pnlGroup} input={handleInputChange} placeholder="Select a PnL Group" required invalid={!!errors.rule.pnlGroup} invalidmessage={errors.rule.pnlGroup} > {pnLGroups.map((group) => ( <McOption key={group} value={group}> {group} </McOption> ))} </McSelect> </div> <div className={styles.gridItem}> <McSelect label="Rule Group" name="ruleGroup" value={ruleData.ruleGroup} input={handleInputChange} placeholder={ruleGroups.length ? "Select a Rule Group" : "Select a PnL Group first"} required disabled={!ruleData.pnlGroup || !ruleGroups.length} invalid={!!errors.rule.ruleGroup} invalidmessage={errors.rule.ruleGroup} > {ruleGroups.map((group) => ( <McOption key={group} value={group}> {group} </McOption> ))} </McSelect> </div> </div> <div className={styles.inputGroup}> <McInput label="Rule Number" name="num" value={ruleData.num} input={handleInputChange} placeholder="Enter rule number" required invalid={!!errors.rule.num} invalidmessage={errors.rule.num} /> </div> <div className={styles.inputGroup}> <McInput label="Rule Name" name="name" value={ruleData.name} input={handleInputChange} placeholder="Enter rule name" required invalid={!!errors.rule.name} invalidmessage={errors.rule.name} /> </div> <div className={styles.inputGroup}> <McInput label="Description" name="desc" value={ruleData.desc} input={handleInputChange} placeholder="Enter rule description" multiline rows={3} required invalid={!!errors.rule.desc} invalidmessage={errors.rule.desc} /> </div> <div className={styles.inputGroup}> <McInput label="Customer Reference ID" name="custRefID" value={ruleData.custRefID} input={handleInputChange} placeholder="Enter customer reference ID" required invalid={!!errors.rule.custRefID} invalidmessage={errors.rule.custRefID} /> </div> </div> ); case 'step': return ( <div className={styles.tabContent}> <h3 className={styles.sectionTitle}>Step Information</h3> {steps.map((step, index) => ( <div key={index} className={styles.stepCase}> <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4> <div className={styles.inputGroup}> <McInput label="Step Number" name="stepNo" value={step.stepNo} input={(e) => handleInputChange(e, index)} placeholder={`Enter step number (e.g., ${ruleData.num}.${index + 1})`} required invalid={!!errors.steps[index].stepNo} invalidmessage={errors.steps[index].stepNo} /> </div> <div className={styles.inputGroup}> <McSelect label="Step Name" name="stepName" value={step.stepName} input={(e) => handleInputChange(e, index)} required placeholder="Select Step Name" invalid={!!errors.steps[index].stepName} invalidmessage={errors.steps[index].stepName} > <McOption value="Single Step">Single Step</McOption> <McOption value="Multi Step">Multi Step</McOption> </McSelect> </div> <div className={styles.inputGroup}> <McInput label="Step Description" name="stepDesc" value={step.stepDesc} input={(e) => handleInputChange(e, index)} placeholder="Enter step description" multiline rows={3} required invalid={!!errors.steps[index].stepDesc} invalidmessage={errors.steps[index].stepDesc} /> </div> <div className={styles.inputGroup}> <McSelect label="Step Type" name="stepType" value={step.stepType} input={(e) => handleInputChange(e, index)} required placeholder="Select Step Type" invalid={!!errors.steps[index].stepType} invalidmessage={errors.steps[index].stepType} > <McOption value="S">S</McOption> <McOption value="M">M</McOption> </McSelect> </div> </div> ))} <div className={styles.stepButtonContainer}> <McButton label="Add Step" appearance="secondary" click={addStep} className={styles.actionButton} /> {steps.length > 1 && ( <McButton label="Remove Step" appearance="neutral" click={() => removeStep(steps.length - 1)} className={styles.actionButton} /> )} </div> </div> ); case 'source': return ( <div className={styles.tabContent}> <h3 className={styles.sectionTitle}>Source Information</h3> {steps.map((step, index) => ( <div key={index} className={styles.stepCase}> <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4> <div className={styles.inputGroup}> <McInput label="Source Table ID" name="sourceTableID" value={step.sourceTableID} input={(e) => handleInputChange(e, index)} placeholder="Enter source table ID" required invalid={!!errors.steps[index].sourceTableID} invalidmessage={errors.steps[index].sourceTableID} /> </div> {step.sourceFilterSets.map((filterSet, filterIndex) => ( <div key={filterIndex} className={styles.filterRow}> <McMultiSelect label="Source Filters" name="sourceFilters" value={filterSet.filters} optionselected={(e) => handleFilterSetChange(e, index, filterIndex, 'source', 'filters')} placeholder="Select source filters" required invalid={!!errors.steps[index].sourceFilterSets[filterIndex]?.filters} invalidmessage={errors.steps[index].sourceFilterSets[filterIndex]?.filters} > {sourceFilterOptions.map((option) => ( <McOption key={option.value} value={option.value}> {option.label} </McOption> ))} </McMultiSelect> <McSelect label="Source Operator" name="sourceOperator" value={filterSet.operator} input={(e) => handleFilterSetChange(e, index, filterIndex, 'source', 'operator')} placeholder="Select an operator" required invalid={!!errors.steps[index].sourceFilterSets[filterIndex]?.operator} invalidmessage={errors.steps[index].sourceFilterSets[filterIndex]?.operator} > {operatorOptions.map((option) => ( <McOption key={option.value} value={option.value}> {option.label} </McOption> ))} </McSelect> <div className={styles.filterValueContainer}> <McMultiSelect label="Source Values" name="sourceValues" value={filterSet.values} optionselected={(e) => handleFilterSetChange(e, index, filterIndex, 'source', 'values')} placeholder="Select source values" required invalid={!!errors.steps[index].sourceFilterSets[filterIndex]?.values} invalidmessage={errors.steps[index].sourceFilterSets[filterIndex]?.values} > {sourceValueOptions.map((option) => ( <McOption key={option.value} value={option.value}> {option.label} </McOption> ))} </McMultiSelect> {step.sourceFilterSets.length > 1 && ( <McButton label="Remove" appearance="neutral" click={() => removeFilterSet(index, filterIndex, 'source')} className={styles.removeButton} /> )} </div> </div> ))} <div className={styles.stepButtonContainer}> <McButton label="Add More" appearance="secondary" click={() => addFilterSet(index, 'source')} className={styles.actionButton} /> </div> </div> ))} </div> ); case 'preAggregate': return ( <div className={styles.tabContent}> <h3 className={styles.sectionTitle}>Pre-Aggregate Columns</h3> {steps.map((step, index) => ( <div key={index} className={styles.stepCase}> <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4> <div className={styles.inputGroup}> <McMultiSelect label="Pre-Aggregator Columns" name="preAggregatorColumns" value={step.preAggregatorColumns} optionselected={(e) => handleMultiSelectChange(e, index, 'preAggregatorColumns')} placeholder="Select pre-aggregator columns" required invalid={!!errors.steps[index].preAggregatorColumns} invalidmessage={errors.steps[index].preAggregatorColumns} > {preAggregatorOptions.map((option) => ( <McOption key={option.value} value={option.value}> {option.label} </McOption> ))} </McMultiSelect> </div> </div> ))} </div> ); case 'join': return ( <div className={styles.tabContent}> <h3 className={styles.sectionTitle}>Join Columns</h3> {steps.map((step, index) => ( <div key={index} className={styles.stepCase}> <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4> <div className={styles.inputGroup}> <McMultiSelect label="Join Columns" name="joinColumns" value={step.joinColumns} optionselected={(e) => handleMultiSelectChange(e, index, 'joinColumns')} placeholder="Select join columns" required invalid={!!errors.steps[index].joinColumns} invalidmessage={errors.steps[index].joinColumns} > {joinColumnsOptions.map((option) => ( <McOption key={option.value} value={option.value}> {option.label} </McOption> ))} </McMultiSelect> </div> </div> ))} </div> ); case 'allocation': return ( <div className={styles.tabContent}> <h3 className={styles.sectionTitle}>Allocation Columns</h3> {steps.map((step, index) => ( <div key={index} className={styles.stepCase}> <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4> <div className={styles.inputGroup}> <McMultiSelect label="Allocation Columns" name="allocationColumns" value={step.allocationColumns} optionselected={(e) => handleMultiSelectChange(e, index, 'allocationColumns')} placeholder="Select allocation columns" required invalid={!!errors.steps[index].allocationColumns} invalidmessage={errors.steps[index].allocationColumns} > {allocationColumnsOptions.map((option) => ( <McOption key={option.value} value={option.value}> {option.label} </McOption> ))} </McMultiSelect> </div> </div> ))} </div> ); case 'driver': return ( <div className={styles.tabContent}> <h3 className={styles.sectionTitle}>Driver Information</h3> {steps.map((step, index) => ( <div key={index} className={styles.stepCase}> <h4 style={{ color: '#35B0CB' }}>STEP {index + 1}</h4> <div className={styles.inputGroup}> <McInput label="Driver Table ID" name="driverTableID" value={step.driverTableID} input={(e) => handleInputChange(e, index)} placeholder="Enter driver table ID" required invalid={!!errors.steps[index].driverTableID} invalidmessage={errors.steps[index].driverTableID} /> </div> <div className={styles.inputGroup}> <McInput label="Driver Weight Column" name="driverWeightColumn" value={step.driverWeightColumn} input={(e) => handleInputChange(e, index)} placeholder="Enter driver weight column" required invalid={!!errors.steps[index].driverWeightColumn} invalidmessage={errors.steps[index].driverWeightColumn} /> </div> {step.driverFilterSets.map((filterSet, filterIndex) => ( <div key={filterIndex} className={styles.filterRow}> <McMultiSelect label="Driver Filters" name="driverFilters" value={filterSet.filters} optionselected={(e) => handleFilterSetChange(e, index, filterIndex, 'driver', 'filters')} placeholder="Select driver filters" required invalid={!!errors.steps[index].driverFilterSets[filterIndex]?.filters} invalidmessage={errors.steps[index].driverFilterSets[filterIndex]?.filters} > {driverFilterOptions.map((option) => ( <McOption key={option.value} value={option.value}> {option.label} </McOption> ))} </McMultiSelect> <McSelect label="Driver Operator" name="driverOperator" value={filterSet.operator} input={(e) => handleFilterSetChange(e, index, filterIndex, 'driver', 'operator')} placeholder="Select an operator" required invalid={!!errors.steps[index].driverFilterSets[filterIndex]?.operator} invalidmessage={errors.steps[index].driverFilterSets[filterIndex]?.operator} > {operatorOptions.map((option) => ( <McOption key={option.value} value={option.value}> {option.label} </McOption> ))} </McSelect> <div className={styles.filterValueContainer}> <McMultiSelect label="Driver Values" name="driverValues" value={filterSet.values} optionselected={(e) => handleFilterSetChange(e, index, filterIndex, 'driver', 'values')} placeholder="Select driver values" required invalid={!!errors.steps[index].driverFilterSets[filterIndex]?.values} invalidmessage={errors.steps[index].driverFilterSets[filterIndex]?.values} > {driverValueOptions.map((option) => ( <McOption key={option.value} value={option.value}> {option.label} </McOption> ))} </McMultiSelect> {step.driverFilterSets.length > 1 && ( <McButton label="Remove" appearance="neutral" click={() => removeFilterSet(index, filterIndex, 'driver')} className={styles.removeButton} /> )} </div> </div> ))} <div className={styles.stepButtonContainer}> <McButton label="Add More" appearance="secondary" click={() => addFilterSet(index, 'driver')} className={styles.actionButton} /> </div> </div> ))} </div> ); default: return <div className={styles.tabContent}>No Tab Selected</div>; } }; return ( <ErrorBoundary> <div className={styles.pageWrapper}> {isLoading && ( <div className={styles.loader}>Loading...</div> )} <div className={styles.container}> <div className={styles.card}> <div className={styles.buttonContainer}> <McButton label="Back" appearance="neutral" click={handleCancel} className={styles.actionButton} /> <McButton label="Save" appearance="primary" click={handleSave} className={styles.actionButton} loading={isLoading} disabled={isLoading} /> </div> <div className={styles.tabs}> <button className={`${styles.tabButton} ${activeTab === 'ruleInfo' ? styles.activeTab : ''}`} onClick={() => setActiveTab('ruleInfo')} > Rule Info </button> <button className={`${styles.tabButton} ${activeTab === 'step' ? styles.activeTab : ''}`} onClick={() => setActiveTab('step')} > Step </button> <button className={`${styles.tabButton} ${activeTab === 'source' ? styles.activeTab : ''}`} onClick={() => setActiveTab('source')} > Source </button> <button className={`${styles.tabButton} ${activeTab === 'preAggregate' ? styles.activeTab : ''}`} onClick={() => setActiveTab('preAggregate')} > Pre-Aggregate </button> <button className={`${styles.tabButton} ${activeTab === 'join' ? styles.activeTab : ''}`} onClick={() => setActiveTab('join')} > Join </button> <button className={`${styles.tabButton} ${activeTab === 'allocation' ? styles.activeTab : ''}`} onClick={() => setActiveTab('allocation')} > Allocation </button> <button className={`${styles.tabButton} ${activeTab === 'driver' ? styles.activeTab : ''}`} onClick={() => setActiveTab('driver')} > Driver </button> </div> {renderTabContent()} </div> </div> </div> </ErrorBoundary> ); }; export default CreateRules;
Comments