Skip to content

Commit ed625d0

Browse files
authored
Merge pull request #149 from mercedes-benz/develop
VULCAN-947/Checkbox disable feature production deployment
2 parents c9e2899 + f20b60f commit ed625d0

File tree

5 files changed

+316
-37
lines changed

5 files changed

+316
-37
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

+49
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,55 @@ 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+
let fieldValue = row[condition.field];
34+
35+
// Handle Neo4j integer format
36+
if (fieldValue && typeof fieldValue === 'object' && 'low' in fieldValue && 'high' in fieldValue) {
37+
// Assuming we only care about the 'low' value for comparisons
38+
fieldValue = String(fieldValue.low);
39+
}
40+
41+
switch (condition.condition) {
42+
case '=':
43+
return fieldValue === condition.value;
44+
case '!=':
45+
return fieldValue !== condition.value;
46+
case 'contains':
47+
return typeof fieldValue === 'string' && fieldValue.includes(condition.value);
48+
case 'not_contains':
49+
return typeof fieldValue === 'string' && !fieldValue.includes(condition.value);
50+
default:
51+
return false;
52+
}
53+
};
54+
655
export const getCheckboxes = (actionsRules, rows, getGlobalParameter) => {
756
let rules = actionsRules.filter((rule) => rule.condition && rule.condition == 'rowCheck');
857
const params = rules.map((rule) => `neodash_${rule.customizationValue}`);

src/chart/table/TableChart.tsx

+88-36
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,
@@ -17,14 +17,21 @@ import {
1717
performActionOnElement,
1818
} from '../../extensions/advancedcharts/Utils';
1919
import { IconButton } from '@neo4j-ndl/react';
20-
import { CloudArrowDownIconOutline, XMarkIconOutline } from '@neo4j-ndl/react/icons';
20+
import { CloudArrowDownIconOutline, ArrowPathIconOutline, XMarkIconOutline } from '@neo4j-ndl/react/icons';
2121
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';
34+
import Notification from '../../component/custom/Notification';
2835

2936
const TABLE_HEADER_HEIGHT = 32;
3037
const TABLE_FOOTER_HEIGHT = 62;
@@ -96,6 +103,10 @@ export const NeoTableChart = (props: ChartProps) => {
96103
extensionEnabled(props.extensions, 'actions') && props.settings && props.settings.actionsRules
97104
? props.settings.actionsRules
98105
: [];
106+
const preConditions =
107+
extensionEnabled(props.extensions, 'actions') && props.settings && props.settings.preConditions
108+
? props.settings.preConditions
109+
: [];
99110
const compact = props.settings && props.settings.compact !== undefined ? props.settings.compact : false;
100111
const styleRules = useStyleRules(
101112
extensionEnabled(props.extensions, 'styling'),
@@ -129,6 +140,7 @@ export const NeoTableChart = (props: ChartProps) => {
129140
}
130141

131142
const { records } = props;
143+
const isApiSpecEnabled = props.settings?.apiSpec && props.settings?.apiSpec.apiEnabled;
132144

133145
const generateSafeColumnKey = (key) => {
134146
return key != 'id' ? key : `${key} `;
@@ -142,6 +154,13 @@ export const NeoTableChart = (props: ChartProps) => {
142154
setAnchorEl(null);
143155
};
144156

157+
const [alertOpen, setAlertOpen] = React.useState(false);
158+
const [notificationMessage, setNotificationMessage] = React.useState('');
159+
const [notificationSeverity, setNotificationSeverity] = React.useState<'success' | 'warning' | 'error'>('success');
160+
161+
const handleNotificationClose = () => {
162+
setAlertOpen(false);
163+
};
145164
const lineBreakColumns: string[] = props.settings?.lineBreaksAfterListEntry;
146165

147166
const actionableFields = actionsRules.filter((r) => r.condition !== 'rowCheck').map((r) => r.field);
@@ -333,60 +352,92 @@ export const NeoTableChart = (props: ChartProps) => {
333352
throw new Error(`Unsupported method: ${method}`);
334353
}
335354

336-
props.updateReportSetting('apiSpec', { ...props.settings.apiSpec, response });
355+
props.updateReportSetting('apiSpec', { ...props.settings?.apiSpec, response });
356+
setNotificationMessage('RUPS package created. Please find the link above');
357+
setNotificationSeverity('success');
358+
setAlertOpen(true);
337359
} catch (error) {
338360
// Handle errors here
339361
console.error('API call error:', error);
362+
setNotificationMessage('RUPS package creation is currently not working. Please try again later.');
363+
setNotificationSeverity('error');
364+
setAlertOpen(true);
340365
} finally {
341366
setApiLoading(false);
342367
}
343368
};
344369

370+
const handleResetApiResponse = () => {
371+
props.updateReportSetting('apiSpec', { ...props.settings?.apiSpec, response: null });
372+
};
373+
374+
useEffect(() => {
375+
if (isApiSpecEnabled) {
376+
handleResetApiResponse();
377+
}
378+
}, [records]);
379+
345380
const apiCallButton = () => (
346381
<Stack direction='row' spacing={2} justifyContent='flex-end' marginRight={2}>
347-
<ButtonGroup color='primary' variant='outlined' aria-label='button group'>
348-
<Button size='small' onClick={handleApiCall} disabled={isApiLoading}>
349-
{isApiLoading ? 'Loading...' : props.settings?.sendRequestButtonName || 'send'}
350-
</Button>
351-
<Button size='small' onClick={handlePopHoverClick} disabled={!props.settings.apiSpec.response}>
382+
<Button variant='outlined' size='small' onClick={handleApiCall} disabled={isApiLoading}>
383+
{isApiLoading ? 'Loading...' : props.settings?.sendRequestButtonName || 'send'}
384+
</Button>
385+
{props.settings?.apiSpec.response && (
386+
<Button
387+
size='small'
388+
variant='outlined'
389+
onClick={handlePopHoverClick}
390+
disabled={!props.settings?.apiSpec.response}
391+
>
352392
{isApiLoading ? 'Loading...' : props.settings?.viewResponseButtonName || 'view response'}
353393
</Button>
354-
{props.settings.apiSpec.response ? (
355-
<Popover
356-
id={id}
357-
open={open}
358-
anchorEl={anchorEl}
359-
onClose={handlePopHoverClose}
360-
anchorOrigin={{
361-
vertical: 'bottom',
362-
horizontal: 'left',
363-
}}
364-
transformOrigin={{
365-
vertical: 'top',
366-
horizontal: 'left',
367-
}}
368-
>
369-
<Typography sx={{ p: 2 }}>
370-
<a href={props.settings?.apiSpec.response.data} target='_blank'>
371-
{props.settings?.apiSpec.response.data}
372-
</a>
373-
</Typography>
374-
</Popover>
375-
) : (
376-
<></>
377-
)}
378-
</ButtonGroup>
394+
)}
395+
{props.settings?.apiSpec.response ? (
396+
<Popover
397+
id={id}
398+
open={open}
399+
anchorEl={anchorEl}
400+
onClose={handlePopHoverClose}
401+
anchorOrigin={{
402+
vertical: 'bottom',
403+
horizontal: 'left',
404+
}}
405+
transformOrigin={{
406+
vertical: 'top',
407+
horizontal: 'left',
408+
}}
409+
>
410+
<Typography sx={{ p: 2 }}>
411+
<a href={props.settings?.apiSpec.response.data} target='_blank'>
412+
{props.settings?.apiSpec.response.data}
413+
</a>
414+
</Typography>
415+
</Popover>
416+
) : (
417+
<></>
418+
)}
379419
</Stack>
380420
);
381421

382-
const isApiSpecEnabled = props.settings?.apiSpec && props.settings?.apiSpec.apiEnabled;
383-
384422
const tableStyle: any = isApiSpecEnabled
385423
? { marginTop: 10, height: '90%', width: '100%', position: 'relative' }
386424
: { height: '100%', width: '100%', position: 'relative' };
387425

426+
const isRowSelectable = (params: { id: GridRowId; row: any }) => {
427+
if (hasPreCondition(preConditions)) {
428+
return convertConditionsToExpression(preConditions, params.row);
429+
}
430+
return true;
431+
};
432+
388433
return (
389434
<ThemeProvider theme={theme}>
435+
<Notification
436+
open={alertOpen}
437+
message={notificationMessage}
438+
severity={notificationSeverity}
439+
onClose={handleNotificationClose}
440+
/>
390441
{isApiSpecEnabled ? apiCallButton() : <></>}
391442
<div className={classes.root} style={tableStyle}>
392443
<Snackbar
@@ -455,6 +506,7 @@ export const NeoTableChart = (props: ChartProps) => {
455506
onSelectionModelChange={(selection) =>
456507
updateCheckBoxes(actionsRules, rows, selection, props.setGlobalParameter)
457508
}
509+
isRowSelectable={isRowSelectable}
458510
autoPageSize
459511
pagination
460512
disableSelectionOnClick

src/component/custom/Notification.tsx

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from 'react';
2+
import Snackbar from '@mui/material/Snackbar';
3+
import MuiAlert, { AlertProps } from '@mui/material/Alert';
4+
5+
interface NotificationProps {
6+
open: boolean;
7+
message: string;
8+
severity: 'success' | 'warning' | 'error';
9+
onClose: () => void;
10+
}
11+
12+
const Alert = React.forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
13+
return <MuiAlert elevation={6} ref={ref} variant='filled' {...props} />;
14+
});
15+
16+
const Notification: React.FC<NotificationProps> = ({ open, message, severity, onClose }) => {
17+
return (
18+
<Snackbar open={open} autoHideDuration={6000} onClose={onClose}>
19+
<Alert onClose={onClose} severity={severity}>
20+
{message}
21+
</Alert>
22+
</Snackbar>
23+
);
24+
};
25+
26+
export default Notification;

0 commit comments

Comments
 (0)