Skip to content

Commit 958cd79

Browse files
authored
Merge pull request #147 from mercedes-benz/VULCAN-947/ActionRules
Vulcan 947/Checkbox Disable Pre Condition
2 parents 4f1710c + e45c07d commit 958cd79

File tree

4 files changed

+216
-5
lines changed

4 files changed

+216
-5
lines changed

src/card/settings/CardSettingsFooter.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ const NeoCardSettingsFooter = ({
148148
settingValue={reportSettings[actionsToCustomize]}
149149
type={type}
150150
fields={fields}
151+
preConditionsSetting={reportSettings?.preConditions}
151152
customReportActionsModalOpen={customReportActionsModalOpen}
152153
setCustomReportActionsModalOpen={setCustomReportActionsModalOpen}
153154
onReportSettingUpdate={onReportSettingUpdate}

src/chart/table/TableActionsHelper.ts

+42
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,48 @@ export const hasCheckboxes = (actionsRules) => {
33
return rules.length > 0;
44
};
55

6+
export const hasPreCondition = (preConditions) => {
7+
return preConditions.length > 0;
8+
};
9+
10+
const groupConditionsByField = (conditions) => {
11+
return conditions.reduce((acc, condition) => {
12+
if (!acc[condition.field]) {
13+
acc[condition.field] = [];
14+
}
15+
acc[condition.field].push(condition);
16+
return acc;
17+
}, {});
18+
};
19+
20+
const evaluateGroupedConditions = (groupedConditions, row) => {
21+
return Object.keys(groupedConditions).every((field) => {
22+
// Logical OR between conditions for the same field
23+
return groupedConditions[field].some((condition) => evaluateCondition(condition, row));
24+
});
25+
};
26+
27+
export const convertConditionsToExpression = (conditions, row) => {
28+
const groupedConditions = groupConditionsByField(conditions);
29+
return !evaluateGroupedConditions(groupedConditions, row);
30+
};
31+
32+
const evaluateCondition = (condition, row) => {
33+
const fieldValue = row[condition.field];
34+
switch (condition.condition) {
35+
case '=':
36+
return fieldValue === condition.value;
37+
case '!=':
38+
return fieldValue !== condition.value;
39+
case 'contains':
40+
return typeof fieldValue === 'string' && fieldValue.includes(condition.value);
41+
case 'not_contains':
42+
return typeof fieldValue === 'string' && !fieldValue.includes(condition.value);
43+
default:
44+
return false;
45+
}
46+
};
47+
648
export const getCheckboxes = (actionsRules, rows, getGlobalParameter) => {
749
let rules = actionsRules.filter((rule) => rule.condition && rule.condition == 'rowCheck');
850
const params = rules.map((rule) => `neodash_${rule.customizationValue}`);

src/chart/table/TableChart.tsx

+20-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useEffect } from 'react';
2-
import { DataGrid, GridColumnVisibilityModel } from '@mui/x-data-grid';
2+
import { DataGrid, GridColumnVisibilityModel, GridRowId } from '@mui/x-data-grid';
33
import { ChartProps } from '../Chart';
44
import {
55
evaluateRulesOnDict,
@@ -22,7 +22,13 @@ import { ThemeProvider, createTheme } from '@mui/material/styles';
2222
import Button from '@mui/material/Button';
2323
import { extensionEnabled } from '../../utils/ReportUtils';
2424
import { renderCellExpand } from '../../component/misc/DataGridExpandRenderer';
25-
import { getCheckboxes, hasCheckboxes, updateCheckBoxes } from './TableActionsHelper';
25+
import {
26+
convertConditionsToExpression,
27+
getCheckboxes,
28+
hasCheckboxes,
29+
hasPreCondition,
30+
updateCheckBoxes,
31+
} from './TableActionsHelper';
2632
import ApiService from '../../utils/apiService';
2733
import { AxiosResponse } from 'axios';
2834
import Notification from '../../component/custom/Notification';
@@ -97,6 +103,10 @@ export const NeoTableChart = (props: ChartProps) => {
97103
extensionEnabled(props.extensions, 'actions') && props.settings && props.settings.actionsRules
98104
? props.settings.actionsRules
99105
: [];
106+
const preConditions =
107+
extensionEnabled(props.extensions, 'actions') && props.settings && props.settings.preConditions
108+
? props.settings.preConditions
109+
: [];
100110
const compact = props.settings && props.settings.compact !== undefined ? props.settings.compact : false;
101111
const styleRules = useStyleRules(
102112
extensionEnabled(props.extensions, 'styling'),
@@ -413,6 +423,13 @@ export const NeoTableChart = (props: ChartProps) => {
413423
? { marginTop: 10, height: '90%', width: '100%', position: 'relative' }
414424
: { height: '100%', width: '100%', position: 'relative' };
415425

426+
const isRowSelectable = (params: { id: GridRowId; row: any }) => {
427+
if (hasPreCondition(preConditions)) {
428+
return convertConditionsToExpression(preConditions, params.row);
429+
}
430+
return true;
431+
};
432+
416433
return (
417434
<ThemeProvider theme={theme}>
418435
<Notification
@@ -489,6 +506,7 @@ export const NeoTableChart = (props: ChartProps) => {
489506
onSelectionModelChange={(selection) =>
490507
updateCheckBoxes(actionsRules, rows, selection, props.setGlobalParameter)
491508
}
509+
isRowSelectable={isRowSelectable}
492510
autoPageSize
493511
pagination
494512
disableSelectionOnClick

src/extensions/actions/ActionsRuleCreationModal.tsx

+153-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,34 @@ import {
88
} from '@neo4j-ndl/react/icons';
99
import { getPageNumbersAndNamesList } from '../advancedcharts/Utils';
1010
import { IconButton, Button, Dialog, Dropdown, TextInput } from '@neo4j-ndl/react';
11-
import { Autocomplete, TextField } from '@mui/material';
11+
import { Autocomplete, TextField, Typography } from '@mui/material';
12+
13+
// Pre conditions
14+
15+
const PRE_CONDITIONS_RULES = [
16+
{
17+
value: '=',
18+
label: '=',
19+
},
20+
{
21+
value: '!=',
22+
label: '!=',
23+
},
24+
{
25+
value: 'contains',
26+
label: 'contains',
27+
},
28+
{
29+
value: 'not_contains',
30+
label: 'not_contains',
31+
},
32+
];
33+
34+
const defaultPreCondition = {
35+
condition: '=',
36+
field: '',
37+
value: '',
38+
};
1239

1340
// The set of conditional checks that are included in the rule specification.
1441
const RULE_CONDITIONS = {
@@ -166,12 +193,17 @@ export const NeoCustomReportActionsModal = ({
166193
fields,
167194
setCustomReportActionsModalOpen,
168195
onReportSettingUpdate,
196+
preConditionsSetting,
169197
}) => {
170198
// The rule set defined in this modal is updated whenever the setting value is externally changed.
171199
const [rules, setRules] = React.useState([]);
200+
const [preConditions, setPreConditions] = React.useState([defaultPreCondition]);
172201
useEffect(() => {
173202
if (settingValue) {
174203
setRules(settingValue);
204+
if (preConditionsSetting) {
205+
setPreConditions(preConditionsSetting);
206+
}
175207
}
176208
}, [settingValue]);
177209

@@ -183,6 +215,12 @@ export const NeoCustomReportActionsModal = ({
183215
} else {
184216
onReportSettingUpdate(settingName, rules);
185217
}
218+
219+
if (preConditions.length === 0) {
220+
onReportSettingUpdate('preConditions', undefined);
221+
} else {
222+
onReportSettingUpdate('preConditions', preConditions);
223+
}
186224
setCustomReportActionsModalOpen(false);
187225
};
188226

@@ -193,6 +231,10 @@ export const NeoCustomReportActionsModal = ({
193231
setRules(newRules);
194232
};
195233

234+
const updatePreConditionFieldById = (j, field, value) => {
235+
setPreConditions((prevItems) => prevItems.map((item, i) => (i === j ? { ...item, [field]: value } : item)));
236+
};
237+
196238
/**
197239
* Create the list of suggestions used in the autocomplete box of the rule specification window.
198240
* This will be dynamic based on the type of report we are customizing.
@@ -333,7 +375,7 @@ export const NeoCustomReportActionsModal = ({
333375
const td2Styling = (type) => ({ width: type === 'bar' ? '15%' : '30%' });
334376
const td2DropdownClassname = (type) => `n-align-middle n-pr-1 ${type === 'bar' ? 'n-w-full' : 'n-w-2/5'}`;
335377
const td2Autocomplete = (type, index, rule) =>
336-
(type !== 'bar' && rule.condition !== 'rowCheck' ? (
378+
type !== 'bar' && rule.condition !== 'rowCheck' ? (
337379
<Autocomplete
338380
className='n-align-middle n-inline-block n-w-/5'
339381
disableClearable={true}
@@ -364,7 +406,7 @@ export const NeoCustomReportActionsModal = ({
364406
/>
365407
) : (
366408
<></>
367-
));
409+
);
368410
const td4Styling = (type) => ({ width: type === 'bar' ? '45%' : '40%' });
369411
const td4DropdownClassname = 'n-align-middle, n-w-1/3';
370412
const td6Styling = (type) => ({ width: type === 'bar' ? '30%' : '20%' });
@@ -535,6 +577,114 @@ export const NeoCustomReportActionsModal = ({
535577
</td>
536578
</tr>
537579
</table>
580+
581+
<table>
582+
<tr>
583+
<td colSpan={7}>
584+
<tr>
585+
<th colSpan={7} className='n-text-center n-font-bold n-py-2'>
586+
Report Pre Conditions
587+
</th>
588+
</tr>
589+
</td>
590+
</tr>
591+
{preConditions.map((con, i) => {
592+
return (
593+
<tr>
594+
<td width='2.5%' className='n-pr-1'>
595+
<span className='n-pr-1'>{i + 1}.</span>
596+
<span className='n-font-bold'>IF</span>
597+
</td>
598+
<td width='100%'>
599+
<div style={{ border: '2px dashed grey' }} className='n-p-1'>
600+
<Autocomplete
601+
className='n-align-middle n-inline-block n-w-5/12 n-pr-1'
602+
disableClearable={true}
603+
id={`autocomplete-label-type${i}`}
604+
size='small'
605+
noOptionsText='*Specify an exact field name'
606+
options={createFieldVariableSuggestions(null, null, null).filter((e) =>
607+
e.toLowerCase().includes(con.field.toLowerCase())
608+
)}
609+
value={con.field ? con.field : ''}
610+
inputValue={con.field ? con.field : ''}
611+
popupIcon={<></>}
612+
style={{ minWidth: 125 }}
613+
onInputChange={(event, value) => {
614+
updatePreConditionFieldById(i, 'field', value);
615+
}}
616+
onChange={(event, newValue) => {
617+
updatePreConditionFieldById(i, 'field', newValue);
618+
}}
619+
renderInput={(params) => (
620+
<TextField
621+
{...params}
622+
placeholder='Field name...'
623+
InputLabelProps={{ shrink: true }}
624+
style={{ padding: '6px 0 7px' }}
625+
size={'small'}
626+
/>
627+
)}
628+
/>
629+
<Dropdown
630+
type='select'
631+
className='n-align-middle n-w-2/12 n-pr-1'
632+
selectProps={{
633+
onChange: (newValue) => updatePreConditionFieldById(i, 'condition', newValue?.value),
634+
options: PRE_CONDITIONS_RULES.map((option) => ({
635+
label: option.label,
636+
value: option.value,
637+
})),
638+
value: { label: con.condition, value: con.condition },
639+
}}
640+
style={{ minWidth: 70, display: 'inline-block' }}
641+
fluid
642+
/>
643+
<TextInput
644+
className='n-align-middle n-inline-block n-w-5/12'
645+
style={{ minWidth: 100 }}
646+
placeholder='Value...'
647+
value={con.value}
648+
onChange={(e) => updatePreConditionFieldById(i, 'value', e.target.value)}
649+
fluid
650+
></TextInput>
651+
</div>
652+
</td>
653+
654+
<td width='5%'>
655+
<IconButton
656+
aria-label='remove rule'
657+
size='medium'
658+
style={{ marginLeft: 10 }}
659+
floating
660+
onClick={() => {
661+
setPreConditions((prevItems) => prevItems.filter((_, j) => j !== i));
662+
}}
663+
>
664+
<XMarkIconOutline />
665+
</IconButton>
666+
</td>
667+
</tr>
668+
);
669+
})}
670+
671+
<tr>
672+
<td colSpan={7}>
673+
<div className='n-text-center n-mt-1'>
674+
<IconButton
675+
aria-label='add'
676+
size='medium'
677+
floating
678+
onClick={() => {
679+
setPreConditions([...preConditions, defaultPreCondition]);
680+
}}
681+
>
682+
<PlusIconOutline />
683+
</IconButton>
684+
</div>
685+
</td>
686+
</tr>
687+
</table>
538688
</div>
539689
</Dialog.Content>
540690
<Dialog.Actions>

0 commit comments

Comments
 (0)