The Rules component is meticulously crafted to empower Salesforce users, providing them with a seamless experience in effortlessly creating dynamic filter rules for Opportunities.
Harnessing the inherent flexibility of Lightning Web Components, this module stands out by delivering a user-friendly interface that simplifies the process of setting up intricate filters based on various field types. With an intuitive design, users can navigate through the complexities of filter customization with ease, ensuring a streamlined and efficient approach to enhancing their Opportunity management within the Salesforce platform.
There are three components involved - HTML, Javascript & CSS.
Let's break down the structure of the Rules component:
<template>
<template for:each={rules} for:item="rule">
<div key={rule.id} class="rules-container">
<div class="main-label-container">
<h3>Rule {rule.id}</h3>
</div>
<div class="main-combobox-container">
<lightning-combobox data-id={rule.id} class="main-combobox" label="Field" value={rule.selectedRuleField} options={ruleFieldOptions} disabled={rule.isFieldDisabled} onchange={handleRuleFieldChange} dropdown-alignment="auto"></lightning-combobox>
</div>
<div class="text-input-dropdown dynamic-combobox-container">
<lightning-combobox data-id={rule.id} class="dynamic-combobox" label="Operator" value={rule.selectedRuleOperator} options={rule.ruleOperatorOptions} disabled={rule.isOperatorDisabled} onchange={handleOperatorChange} dropdown-alignment="auto">
</lightning-combobox>
</div>
<div class="text-input-container">
<template if:true={rule.isType}>
<lightning-input data-id={rule.id} type={rule.type} variant="standard" formatter={rule.formatter} value={rule.selectedRuleValue} name={rule.selectedFieldCategory} label="Value" disabled={rule.isValueDisabled} placeholder={rule.placeholder} onblur={handleValueBlur} onchange={handleValueChange}></lightning-input>
</template>
<template if:false={rule.isType}>
<lightning-combobox data-id={rule.id} class="rule-value-combobox" label="Value" placeholder="Select Value..." value={rule.selectedRuleValue} options={rule.ruleValueOptions} disabled={rule.isValueDisabled} onchange={handleValueChange} dropdown-alignment="auto">
</lightning-combobox>
</template>
</div>
<div class="remove-rules icon-wrapper">
<lightning-icon class={removeIcon} icon-name="utility:close" alternative-text="removeRule" onclick={handleRemoveRule} data-id={rule.id}></lightning-icon>
</div>
</div>
</template>
<div class="add-rules">
<div class="main-label-container"></div>
<lightning-button variant="base" label="+ Add Rule" title="+ Add Rule" onclick={handleAddRules} class="add-button slds-m-left_x-small"></lightning-button>
</div>
</div>
</template>
import { LightningElement, track, wire } from 'lwc';
import { getObjectInfo } from 'lightning/uiObjectInfoApi';
import OPPORTUNITY_OBJECT from '@salesforce/schema/Opportunity';
const fieldToOperatorsMap = {
string: [
{ label: "Equals", value: "equals" },
{ label: "Does Not Equals", value: "doesNotEquals" },
{ label: "Start With", value: "startWith" },
{ label: "End With", value: "endWith" },
{ label: "Contains", value: "contains" },
{ label: "Is Null", value: "isNull" },
],
integer: [
{ label: "Equals", value: "equals" },
{ label: "Does Not Equals", value: "doesNotEquals" },
{ label: "Greater Than", value: "greaterThan" },
{ label: "Less Than", value: "lessThan" },
{ label: "Greater Than Or Equal", value: "greaterThanOrEqual" },
{ label: "Less Than Or Equal", value: "lessThanOrEqual" },
{ label: "Contains", value: "contains" },
{ label: "Is Null", value: "isNull" },
],
currency: [
{ label: "Equals", value: "equals" },
{ label: "Does Not Equals", value: "doesNotEquals" },
{ label: "Greater Than", value: "greaterThan" },
{ label: "Less Than", value: "lessThan" },
{ label: "Greater Than Or Equal", value: "greaterThanOrEqual" },
{ label: "Less Than Or Equal", value: "lessThanOrEqual" },
{ label: "Is Null", value: "isNull" },
],
boolean: [
{ label: "Equals", value: "equals" },
{ label: "Does Not Equals", value: "doesNotEquals" },
{ label: "Is Null", value: "isNull" },
],
picklist: [
{ label: "Equals", value: "equals" },
{ label: "Does Not Equals", value: "doesNotEquals" },
{ label: "Contains", value: "contains" },
],
date: [
{ label: "Equals", value: "equals" },
{ label: "Does Not Equals", value: "doesNotEquals" },
{ label: "Greater Than", value: "greaterThan" },
{ label: "Less Than", value: "lessThan" },
{ label: "Greater Than Or Equal", value: "greaterThanOrEqual" },
{ label: "Less Than Or Equal", value: "lessThanOrEqual" },
{ label: "Is Null", value: "isNull" },
],
percent: [
{ label: "Equals", value: "equals" },
{ label: "Does Not Equals", value: "doesNotEquals" },
{ label: "Greater Than", value: "greaterThan" },
{ label: "Less Than", value: "lessThan" },
{ label: "Greater Than Or Equal", value: "greaterThanOrEqual" },
{ label: "Less Than Or Equal", value: "lessThanOrEqual" },
{ label: "Is Null", value: "isNull" },
],
};
const ruleValueOptions = {
equals: [
{ label: 'True', value: 'True' },
{ label: 'False', value: 'False' }
],
doesNotEquals: [
{ label: 'True', value: 'True' },
{ label: 'False', value: 'False' }
],
isNull: [
{ label: 'True', value: 'True' },
{ label: 'False', value: 'False' }
],
}
export default class Rules extends LightningElement {
ruleFieldOptions = [];
ruleOperatorOptions = [];
ruleValueOptions = [];
selectedFieldDataDetails = [];
@track rules = [
{
id: 1,
selectedRuleField: null,
selectedRuleOperator: null,
selectedRuleValue: null,
isFieldDisabled: false,
isOperatorDisabled: true,
isValueDisabled: true,
selectedFieldCategory: 'string',
selectedOperatorCategory: 'equals',
type: 'text',
placeholder: 'Enter a value',
},
];
@wire(getObjectInfo, { objectApiName: OPPORTUNITY_OBJECT })
wiredObjectInfo({ error, data }) {
if (data) {
const allowedDataTypes = ['String', 'Currency',];
this.ruleFieldOptions = Object.values(data.fields)
.filter(field => allowedDataTypes.includes(field.dataType))
.map(field => ({
label: `${field.label} (${field.dataType})`,
value: field.apiName,
dataType: field.dataType
}));
} else if (error) {
console.error(error);
}
}
shouldRenderInput() {
const hasRules = this.rules && this.rules.length > 0;
if (hasRules) {
// Check if any rule satisfies the conditions
return this.rules.some(rule => {
const isNotNullOrChange = !(rule.selectedOperatorCategory === 'isNull' || rule.selectedOperatorCategory === 'isChanged');
const isStringType = rule.selectedFieldCategory.toLowerCase() === 'string';
const isNumberType = rule.selectedFieldCategory.toLowerCase() === 'currency';
const isDateType = rule.selectedFieldCategory.toLowerCase() === 'date';
return isNotNullOrChange && (isStringType || isNumberType || isDateType);
});
}
return false;
}
get inputType() {
return this.defaultValueType.selectedFieldCategory.toLowerCase() === 'date'
? 'date'
: 'text';
}
selectedFieldDataType(value) {
this.selectedFieldDataPattern = this.ruleFieldOptions.find((field) => field.value === value);
return this.selectedFieldDataPattern ? this.selectedFieldDataPattern.dataType : "";
}
handleRuleFieldChange(event) {
const ruleId = event.target.dataset.id;
const selectedFieldOption = event.detail.value;
let rules = this.rules.map((rule) => {
if (rule.id == ruleId) {
let selectedField = this.selectedFieldDataType(selectedFieldOption);
let fieldLabel = this.selectedFieldDataPattern.label;
let selectedOperatorOptions = fieldToOperatorsMap[selectedField.toLowerCase()] || [];
return {
...rule,
selectedRuleField: selectedFieldOption,
selectedFieldLabel: fieldLabel,
selectedRuleOperator: "",
selectedRuleValue: "",
isOperatorDisabled: false,
isValueDisabled: true,
selectedFieldCategory: selectedField,
ruleOperatorOptions: selectedOperatorOptions,
};
} else {
return rule;
}
});
this.rules = rules;
}
handleOperatorChange(event) {
const ruleId = event.target.dataset.id;
const selectedOperatorOption = event.detail.value;
let rules = this.rules.map((rule) => {
if (rule.id == ruleId) {
let selectedField = this.selectedFieldDataType(rule.selectedRuleField);
const type = selectedField.toLowerCase();
let isPicklistType = this.selectedFieldDataPattern.isValuePicklistType;
let picklistOptions = this.selectedFieldDataPattern.picklistTypeOptions;
const isNullOrChange = selectedOperatorOption === "isNull";
const isValueInputType = ['string','integer','currency', 'percent', 'date'].includes(type.toLowerCase()) && !isNullOrChange ? true : false;
if (!isValueInputType && !isPicklistType || (isValueInputType && isNullOrChange)) {
return {
...rule,
selectedRuleOperator: selectedOperatorOption,
selectedRuleValue: "",
isValuePicklistType: isPicklistType,
isValueDisabled: false,
ruleValueOptions: ruleValueOptions[selectedOperatorOption] || [],
isType: isValueInputType,
};
} else if (!isValueInputType && isPicklistType) {
return {
...rule,
selectedRuleOperator: selectedOperatorOption,
selectedRuleValue: "",
isValuePicklistType: isPicklistType,
isValueDisabled: false,
ruleValueOptions: picklistOptions || [],
isType: isValueInputType,
};
} else {
return {
...rule,
selectedRuleOperator: selectedOperatorOption,
selectedRuleValue: "",
isValuePicklistType: isPicklistType,
isValueDisabled: false,
selectedOperatorCategory: type,
type: type === "string" ? "text" : ['integer','currency','percent'].includes(type.toLowerCase()) ? 'number': type,
placeholder: type === "date" ? "DD-MMM-YYYY" : "Enter a value",
isType: isValueInputType,
formatter: ['currency','percent'].includes(type.toLowerCase()) ? type: '',
};
}
} else {
return rule;
}
});
this.rules = rules;
}
handleValueChange(event) {
const ruleId = event.target.dataset.id;
const selectedValueOption = event.detail.value;
let updateRule = {};
let rules = this.rules.map((rule) => {
if (rule.id == ruleId) {
return {
...rule,
selectedRuleValue: selectedValueOption,
};
} else {
return rule;
}
});
this.rules = rules;
}
handleRemoveRule(event) {
let ruleId = event.target.dataset.id;
let defaultRules = JSON.parse(JSON.stringify((this.rules)));
if (defaultRules.length > 1) {
defaultRules.splice(ruleId - 1, 1);
defaultRules.forEach(function (item, index) {
item.id = index + 1;
});
this.rules = defaultRules;
}
}
handleAddRules() {
const newRule = {
id: this.rules.length + 1,
selectedRuleField: null,
selectedFieldLabel: '',
selectedRuleOperator: null,
selectedRuleValue: null,
isFieldDisabled: false,
isOperatorDisabled: true,
isValueDisabled: true,
ruleOperatorOptions: [],
selectedFieldCategory: "string",
selectedOperatorCategory: "equals",
type: "text",
placeholder: "Enter a value",
isType: true,
};
this.rules.push(newRule);
}
}
.rules-container {
display: flex;
align-items: center;
width: 100%;
}
.main-label-container {
margin: 17px 0px 0px 20px;
width: 6%;
}
.rule-logic-label {
margin: 17px 0px 0px 20px;
}
.main-combobox-container {
margin: 0 10px;
width: 34.5% !important;
}
.main-rulelogic-container {
margin: 0 10px;
width: 95% !important;
}
.dynamic-content-container {
margin: 0 10px;
width: 12%;
}
.text-input-dropdown {
margin: 0px 10px 0px 0px;
width: 15%;
/* line-height: 10px; */
}
.remove-rules {
margin: 23px 0 0 10px;
width: 3%;
}
.add-rules {
line-height: 30px;
font-size: 15px;
margin: 10px 0px -10px 0px;
display: flex;
}
.rules-container-box .icon-wrapper {
margin-top: 15px;
}
.rules-container-box {
display: grid;
align-items: center;
grid-template-columns: 7% 63.2% auto;
grid-gap: 10px;
}
The Rules Lightning Web Component provides a powerful and flexible solution for creating dynamic filter rules within Salesforce.
Its dynamic field selection, real-time updates, and user-friendly interface make it a valuable tool for enhancing the filtering capabilities of your Salesforce Opportunities.