import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { McButton, McInput } from '@maersk-global/mds-react-wrapper';
import { McSelect } from '@maersk-global/mds-react-wrapper/components-core/mc-select';
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';
const CreateRules = () => {
const navigate = useNavigate();
const [activeTab, setActiveTab] = useState('ruleInfo');
const [ruleData, setRuleData] = useState({
num: '',
name: '',
desc: '',
custRefID: '',
ruleGroup: '',
isActive: 'Y',
pnlGroup: '',
});
const [steps, setSteps] = useState([
{
stepNo: '',
stepName: 'Single Step',
StepDesc: '',
stepType: 'S',
preAggregatorColumns: '',
sourceTable: '',
sourceFilters: '',
joinColumns: '',
allocationColumns: '',
driverTableID: '',
driverWeightColumn: '',
driverFilters: '',
},
]);
const [errors, setErrors] = useState({ rule: {}, steps: [{}] });
// Extract PnL Group options from JSON, with fallback for empty data
const pnlGroups = data.PnLGroups ? Object.keys(data.PnLGroups) : [];
// Get Rule Group options based on selected PnL Group, with fallback
const ruleGroups = ruleData.pnlGroup && data.PnLGroups[ruleData.pnlGroup]
? data.PnLGroups[ruleData.pnlGroup].RuleGroups || []
: [];
const addStep = () => {
setSteps((prevSteps) => [
...prevSteps,
{
stepNo: '',
stepName: '',
StepDesc: '',
stepType: '',
preAggregatorColumns: '',
sourceTable: '',
sourceFilters: '',
joinColumns: '',
allocationColumns: '',
driverTableID: '',
driverWeightColumn: '',
driverFilters: '',
},
]);
setErrors((prevErrors) => ({
...prevErrors,
steps: [...prevErrors.steps, {}],
}));
};
const validateForm = () => {
const newErrors = { rule: {}, steps: steps.map(() => ({})) };
let isValid = true;
// Validate rule data (all fields are mandatory)
Object.keys(ruleData).forEach((key) => {
if (!ruleData[key]) {
newErrors.rule[key] = 'This field is required';
isValid = false;
}
});
// Validate steps (all fields are mandatory)
steps.forEach((step, index) => {
const stepErrors = {};
Object.keys(step).forEach((key) => {
if (!step[key]) {
stepErrors[key] = 'This field is required';
isValid = false;
}
});
newErrors.steps[index] = stepErrors;
});
setErrors(newErrors);
return isValid;
};
const handleInputChange = (e, stepIndex = null) => {
const { name, value } = e.target;
console.log(`Input changed: ${name} = ${value}${stepIndex !== null ? ` (Step ${stepIndex + 1})` : ''}`);
if (stepIndex !== null) {
setSteps((prevSteps) => {
const newSteps = [...prevSteps];
newSteps[stepIndex] = { ...newSteps[stepIndex], [name]: value };
return newSteps;
});
setErrors((prevErrors) => ({
...prevErrors,
steps: prevErrors.steps.map((stepErrors, i) =>
i === stepIndex ? { ...stepErrors, [name]: '' } : stepErrors
),
}));
} else {
setRuleData((prevData) => ({
...prevData,
[name]: value,
...(name === 'pnlGroup' ? { ruleGroup: '' } : {}),
}));
setErrors((prevErrors) => ({
...prevErrors,
rule: { ...prevErrors.rule, [name]: '' },
}));
}
};
const handleSelectChange = (e) => {
const { name, value } = e.target;
console.log(`Select changed: ${name} = ${value}`);
setRuleData((prevData) => ({
...prevData,
[name]: value,
...(name === 'pnlGroup' ? { ruleGroup: '' } : {}),
}));
setErrors((prevErrors) => ({
...prevErrors,
rule: { ...prevErrors.rule, [name]: '' },
}));
};
const handleSave = async () => {
if (!validateForm()) {
console.log('Validation failed:', JSON.stringify(errors, null, 2));
alert('Please fill out all required fields.');
return;
}
// Parse comma-separated columns and filters
const parseColumns = (input) => input.split(',').map((item) => item.trim()).filter((item) => item);
const parseFilters = (input) => {
const filters = input.split(';').map((item) => item.trim()).filter((item) => item);
return filters.map((filter) => {
const [name, filterType, values] = filter.split(':').map((item) => item.trim());
return { name, filterType, values };
});
};
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: '1',
Name: step.sourceTable,
},
sourceFilters: {
columns: parseFilters(step.sourceFilters),
},
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.driverFilters),
},
},
})),
},
],
},
};
console.log('Saving Rule Data:', JSON.stringify(ruleJson, null, 2));
try {
const response = await fetch('/api/rules', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify(ruleJson),
});
if (response.ok) {
console.log('Rule created successfully');
alert('Rule created successfully!');
navigate('/');
} else {
const errorText = await response.text();
console.error('Failed to create rule:', response.status, errorText);
alert(`Failed to create rule: ${errorText || response.statusText}`);
}
} catch (error) {
console.error('Error during API call:', error.message);
alert('An error occurred while saving the rule. Please try again.');
}
};
const handleCancel = () => {
console.log('Cancelling rule creation');
navigate('/');
};
const renderTabContent = () => {
console.log('Rendering tab:', activeTab);
switch (activeTab) {
case 'ruleInfo':
return (
<div className={styles.tabContent}>
<h3 className={styles.sectionTitle}>Rule Information</h3>
<div className={styles.formGrid}>
<div className={styles.gridItem}>
<McSelect
label="PnL Group"
name="pnlGroup"
value={ruleData.pnlGroup}
optionselected={handleSelectChange}
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}
optionselected={handleSelectChange}
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 className={styles.inputGroup}>
<McSelect
label="Is Active"
name="isActive"
value={ruleData.isActive}
optionselected={handleSelectChange}
required
invalid={!!errors.rule.isActive}
invalidmessage={errors.rule.isActive}
>
<McOption value="Y">Yes</McOption>
<McOption value="N">No</McOption>
</McSelect>
</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>Step {index + 1}</h4>
<div className={styles.inputGroup}>
<McInput
minutiae
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}
optionselected={(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}
optionselected={(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.buttonContainer}>
<McButton
label="Add Step"
appearance="secondary"
click={addStep}
className={styles.actionButton}
/>
</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>Step {index + 1}</h4>
<div className={styles.inputGroup}>
<McInput
label="Pre-Aggregator Columns"
name="preAggregatorColumns"
value={step.preAggregatorColumns}
input={(e) => handleInputChange(e, index)}
placeholder="Enter columns (comma-separated)"
multiline
rows={3}
required
invalid={!!errors.steps[index].preAggregatorColumns}
invalidmessage={errors.steps[index].preAggregatorColumns}
/>
</div>
</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>Step {index + 1}</h4>
<div className={styles.inputGroup}>
<McInput
label="Source Table"
name="sourceTable"
value={step.sourceTable}
input={(e) => handleInputChange(e, index)}
placeholder="Enter source table name"
required
invalid={!!errors.steps[index].sourceTable}
invalidmessage={errors.steps[index].sourceTable}
/>
</div>
<div className={styles.inputGroup}>
<McInput
label="Source Filters"
name="sourceFilters"
value={step.sourceFilters}
input={(e) => handleInputChange(e, index)}
placeholder="Enter filters (e.g., PNL_LINE:IN:PnL.DVC.214,PnL.DVC.215;MOVE_TYPE:EQ:EX)"
multiline
rows={3}
required
invalid={!!errors.steps[index].sourceFilters}
invalidmessage={errors.steps[index].sourceFilters}
/>
</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>Step {index + 1}</h4>
<div className={styles.inputGroup}>
<McInput
label="Join Columns"
name="joinColumns"
value={step.joinColumns}
input={(e) => handleInputChange(e, index)}
placeholder="Enter columns (comma-separated)"
multiline
rows={3}
required
invalid={!!errors.steps[index].joinColumns}
invalidmessage={errors.steps[index].joinColumns}
/>
</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>Step {index + 1}</h4>
<div className={styles.inputGroup}>
<McInput
label="Allocation Columns"
name="allocationColumns"
value={step.allocationColumns}
input={(e) => handleInputChange(e, index)}
placeholder="Enter columns (comma-separated)"
multiline
rows={3}
required
invalid={!!errors.steps[index].allocationColumns}
invalidmessage={errors.steps[index].allocationColumns}
/>
</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>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>
<div className={styles.inputGroup}>
<McInput
label="Driver Filters"
name="driverFilters"
value={step.driverFilters}
input={(e) => handleInputChange(e, index)}
placeholder="Enter filters"
multiline
rows={3}
required
invalid={!!errors.steps[index].driverFilters}
invalidmessage={errors.steps[index].driverFilters}
/>
</div>
</div>
))}
</div>
);
default:
return <div className={styles.tabContent}>No Tab Selected</div>;
}
};
return (
<div className={styles.pageWrapper}>
<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}
/>
</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 === 'preAggregate' ? styles.activeTab : ''}`}
onClick={() => setActiveTab('preAggregate')}
>
Pre-Aggregate
</button>
<button
className={`${styles.tabButton} ${activeTab === 'source' ? styles.activeTab : ''}`}
onClick={() => setActiveTab('source')}
>
Source
</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>
);
};
export default CreateRules;