From 5f217273012be092baf6ef3f3f0a2b638467352a Mon Sep 17 00:00:00 2001 From: sniedzielski Date: Fri, 12 Jan 2024 15:30:22 +0100 Subject: [PATCH 1/4] CM-454: added hardcoded summary table of duplications --- .../DeduplicationFieldSelectionDialog.js | 24 ++++- .../dialogs/DeduplicationSummaryDialog.js | 96 +++++++++++++++++++ .../tables/DeduplicationSummaryTable.js | 86 +++++++++++++++++ src/translations/en.json | 6 +- 4 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 src/components/dialogs/DeduplicationSummaryDialog.js create mode 100644 src/components/tables/DeduplicationSummaryTable.js diff --git a/src/components/dialogs/DeduplicationFieldSelectionDialog.js b/src/components/dialogs/DeduplicationFieldSelectionDialog.js index 0a12120..59ea222 100644 --- a/src/components/dialogs/DeduplicationFieldSelectionDialog.js +++ b/src/components/dialogs/DeduplicationFieldSelectionDialog.js @@ -13,6 +13,7 @@ import { withTheme, withStyles } from '@material-ui/core/styles'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import DeduplicationFieldPicker from '../pickers/DeduplicationFieldPicker'; +import DeduplicationSummaryDialog from './DeduplicationSummaryDialog'; const styles = (theme) => ({ item: theme.paper.item, @@ -27,6 +28,7 @@ function DeduplicationFieldSelectionDialog({ const [selectedValues, setSelectedValues] = useState([]); const [isOpen, setIsOpen] = useState(false); + const [showSummaryDialog, setShowSummaryDialog] = useState(false); const handleOpen = () => { setIsOpen(true); @@ -34,12 +36,22 @@ function DeduplicationFieldSelectionDialog({ const handleClose = () => { setIsOpen(false); + setSelectedValues([]); }; const handlePickerChange = (selectedOptions) => { setSelectedValues(selectedOptions); }; + const handleOpenNextDialog = () => { + handleClose(); + setShowSummaryDialog(true); + }; + + const handleSummaryDialogClose = () => { + setShowSummaryDialog(false); + }; + return ( <> @@ -116,6 +129,15 @@ function DeduplicationFieldSelectionDialog({ + + {showSummaryDialog && ( + + )} ); } diff --git a/src/components/dialogs/DeduplicationSummaryDialog.js b/src/components/dialogs/DeduplicationSummaryDialog.js new file mode 100644 index 0000000..4ec1641 --- /dev/null +++ b/src/components/dialogs/DeduplicationSummaryDialog.js @@ -0,0 +1,96 @@ +import React, { useState } from 'react'; +import { injectIntl } from 'react-intl'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import { formatMessage } from '@openimis/fe-core'; +import { withTheme, withStyles } from '@material-ui/core/styles'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import DeduplicationSummaryTable from '../tables/DeduplicationSummaryTable'; + +const styles = (theme) => ({ + item: theme.paper.item, +}); + +function DeduplicationSummaryDialog({ + intl, + benefitPlan, + handleClose, + showSummaryDialog, +}) { + if (!benefitPlan) return null; + + return ( + + + {formatMessage(intl, 'deduplication', 'deduplicate.summary.title')} + + + + + +
+
+ +
+
+ +
+
+
+
+ ); +} + +const mapStateToProps = (state) => ({ + rights: !!state.core && !!state.core.user && !!state.core.user.i_user ? state.core.user.i_user.rights : [], + confirmed: state.core.confirmed, +}); + +const mapDispatchToProps = (dispatch) => bindActionCreators({ +}, dispatch); + +export default injectIntl( + withTheme(withStyles(styles)(connect(mapStateToProps, mapDispatchToProps)(DeduplicationSummaryDialog))), +); diff --git a/src/components/tables/DeduplicationSummaryTable.js b/src/components/tables/DeduplicationSummaryTable.js new file mode 100644 index 0000000..f953c31 --- /dev/null +++ b/src/components/tables/DeduplicationSummaryTable.js @@ -0,0 +1,86 @@ +import React, { useEffect } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; + +import { + TableContainer, TableHead, TableBody, Table, TableCell, TableRow, Paper, +} from '@material-ui/core'; +import { makeStyles } from '@material-ui/styles'; + +import { useModulesManager, ProgressOrError, useTranslations } from '@openimis/fe-core'; +import { MODULE_NAME } from '../../constants'; + +const useStyles = makeStyles((theme) => ({ + footer: { + marginInline: 16, + marginBlock: 12, + }, + headerTitle: theme.table.title, + actionCell: { + width: 60, + }, + header: theme.table.header, +})); + +const DEDUPLICATION_SUMMARY_HEADERS = [ + 'deduplication.deduplicationSummaryTable.group', + 'deduplication.deduplicationSummaryTable.duplicates' +]; + +function DeduplicationSummaryTable() { + //const dispatch = useDispatch(); + const modulesManager = useModulesManager(); + const classes = useStyles(); + const { formatMessage } = useTranslations(MODULE_NAME, modulesManager); + //const { + // fetchingFamilyMembers, familyMembers, errorFamilyMembers + //} = useSelector((store) => store.insuree); + + const results = [ + {group: "Firstname: John, Surname: Doe, DOB: 1995-01-2020", duplicates: 2}, + {group: "Firstname: John, Surname: Test, DOB: 1995-01-2020", duplicates: 4}, + {group: "Firstname: Michael, Surname: Doe, DOB: 1995-01-2020", duplicates: 10}, + {group: "Firstname :Dennis, Surname: Jin, DOB: 1994-01-2020", duplicates: 20}, + ]; + + //useEffect(() => { + //if (!insuree) return; + + //dispatch(fetchFamilyMembers(modulesManager, [`familyUuid: "${insuree.family.uuid}"`])); + //}, [insuree]); + + return ( + + + + + {DEDUPLICATION_SUMMARY_HEADERS.map((header) => ( + + {' '} + {formatMessage(header)} + {' '} + + ))} + + + + {results?.map((result) => ( + + + {' '} + {result.group} + {' '} + + + {' '} + {result.duplicates} + {' '} + + + ))} + +
+
+ ); +} + +export default DeduplicationSummaryTable; diff --git a/src/translations/en.json b/src/translations/en.json index 2682ae4..ceb3e4e 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -4,5 +4,9 @@ "deduplication.deduplicate.button.cancel": "Cancel", "deduplication.deduplicate.fields": "Duplicate Detection Field Selection", "deduplication.deduplicate.fields.placeholder": "Duplicate Detection Field Selection", - "deduplicate.button.showDuplicateSummary": "Show Duplicate Summary" + "deduplication.deduplicate.button.showDuplicateSummary": "Show Duplicate Summary", + "deduplication.deduplicate.summary.title": "Deduplication Summary", + "deduplication.deduplicate.button.createDeduplicationReviewTask": "Create Deduplication Review Tasks", + "deduplication.deduplicationSummaryTable.group": "Group", + "deduplication.deduplicationSummaryTable.duplicates": "Duplicates" } \ No newline at end of file From 7c69f32dbd41946ff46b552b8951616038e40018 Mon Sep 17 00:00:00 2001 From: sniedzielski Date: Tue, 16 Jan 2024 17:18:25 +0100 Subject: [PATCH 2/4] CM-454: connect with the backend --- src/actions.js | 15 +++++ .../DeduplicationFieldSelectionDialog.js | 4 +- .../dialogs/DeduplicationSummaryDialog.js | 14 ++++- .../tables/DeduplicationSummaryTable.js | 37 ++++++------ src/index.js | 2 + src/reducer.js | 56 +++++++++++++++++++ src/util/action-type.js | 5 ++ src/util/styles.js | 24 ++++++++ 8 files changed, 132 insertions(+), 25 deletions(-) create mode 100644 src/actions.js create mode 100644 src/reducer.js create mode 100644 src/util/action-type.js create mode 100644 src/util/styles.js diff --git a/src/actions.js b/src/actions.js new file mode 100644 index 0000000..19eb1a5 --- /dev/null +++ b/src/actions.js @@ -0,0 +1,15 @@ +import { + graphql, + formatQuery, +} from '@openimis/fe-core'; +import { ACTION_TYPE } from './reducer'; + +const DEDUPLICATION_SUMMARY_FULL_PROJECTION = () => [ + 'rows {count, columnValues}', +]; + +// eslint-disable-next-line import/prefer-default-export +export function fetchDeduplicationSummary(params) { + const payload = formatQuery('beneficiaryDeduplicationSummary', params, DEDUPLICATION_SUMMARY_FULL_PROJECTION()); + return graphql(payload, ACTION_TYPE.GET_DEDUPLICATION_SUMMARY); +} diff --git a/src/components/dialogs/DeduplicationFieldSelectionDialog.js b/src/components/dialogs/DeduplicationFieldSelectionDialog.js index 59ea222..fb183b3 100644 --- a/src/components/dialogs/DeduplicationFieldSelectionDialog.js +++ b/src/components/dialogs/DeduplicationFieldSelectionDialog.js @@ -36,7 +36,6 @@ function DeduplicationFieldSelectionDialog({ const handleClose = () => { setIsOpen(false); - setSelectedValues([]); }; const handlePickerChange = (selectedOptions) => { @@ -44,8 +43,8 @@ function DeduplicationFieldSelectionDialog({ }; const handleOpenNextDialog = () => { - handleClose(); setShowSummaryDialog(true); + handleClose(); }; const handleSummaryDialogClose = () => { @@ -136,6 +135,7 @@ function DeduplicationFieldSelectionDialog({ benefitPlan={benefitPlan} handleClose={handleSummaryDialogClose} showSummaryDialog={showSummaryDialog} + selectedValues={selectedValues} /> )} diff --git a/src/components/dialogs/DeduplicationSummaryDialog.js b/src/components/dialogs/DeduplicationSummaryDialog.js index 4ec1641..6c87791 100644 --- a/src/components/dialogs/DeduplicationSummaryDialog.js +++ b/src/components/dialogs/DeduplicationSummaryDialog.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React from 'react'; import { injectIntl } from 'react-intl'; import Button from '@material-ui/core/Button'; import Dialog from '@material-ui/core/Dialog'; @@ -10,6 +10,7 @@ import { withTheme, withStyles } from '@material-ui/core/styles'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import DeduplicationSummaryTable from '../tables/DeduplicationSummaryTable'; +import { fetchDeduplicationSummary } from '../../actions'; const styles = (theme) => ({ item: theme.paper.item, @@ -20,9 +21,14 @@ function DeduplicationSummaryDialog({ benefitPlan, handleClose, showSummaryDialog, + selectedValues, }) { if (!benefitPlan) return null; + // Extract the 'id' values from each object in the array + const columns = selectedValues.map((value) => value.id); + const columnParam = `columns: ${JSON.stringify(columns)}`; + return ( - + ({ const DEDUPLICATION_SUMMARY_HEADERS = [ 'deduplication.deduplicationSummaryTable.group', - 'deduplication.deduplicationSummaryTable.duplicates' + 'deduplication.deduplicationSummaryTable.duplicates', ]; -function DeduplicationSummaryTable() { - //const dispatch = useDispatch(); +function DeduplicationSummaryTable({ + columnParam, benefitPlan, fetchDeduplicationSummary, +}) { + const dispatch = useDispatch(); const modulesManager = useModulesManager(); const classes = useStyles(); const { formatMessage } = useTranslations(MODULE_NAME, modulesManager); - //const { - // fetchingFamilyMembers, familyMembers, errorFamilyMembers - //} = useSelector((store) => store.insuree); + const { + fetchingSummary, summary, errorSummary, + } = useSelector((store) => store.deduplication); - const results = [ - {group: "Firstname: John, Surname: Doe, DOB: 1995-01-2020", duplicates: 2}, - {group: "Firstname: John, Surname: Test, DOB: 1995-01-2020", duplicates: 4}, - {group: "Firstname: Michael, Surname: Doe, DOB: 1995-01-2020", duplicates: 10}, - {group: "Firstname :Dennis, Surname: Jin, DOB: 1994-01-2020", duplicates: 20}, - ]; - - //useEffect(() => { - //if (!insuree) return; - - //dispatch(fetchFamilyMembers(modulesManager, [`familyUuid: "${insuree.family.uuid}"`])); - //}, [insuree]); + useEffect(() => { + const params = [columnParam, `benefitPlanId: "${benefitPlan.id}"`]; + dispatch(fetchDeduplicationSummary(params)); + }, []); return ( @@ -63,16 +57,17 @@ function DeduplicationSummaryTable() { - {results?.map((result) => ( + + {summary?.map((result) => ( {' '} - {result.group} + {result.columnValues} {' '} {' '} - {result.duplicates} + {result.count} {' '} diff --git a/src/index.js b/src/index.js index ae6888e..5d5fdd7 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,10 @@ import messagesEn from './translations/en.json'; import DeduplicationFieldSelectionDialog from './components/dialogs/DeduplicationFieldSelectionDialog'; +import reducer from './reducer'; const DEFAULT_CONFIG = { translations: [{ key: 'en', messages: messagesEn }], + reducers: [{ key: 'deduplication', reducer }], 'deduplication.deduplicationFieldSelectionDialog': [ DeduplicationFieldSelectionDialog, ], diff --git a/src/reducer.js b/src/reducer.js new file mode 100644 index 0000000..f5d6010 --- /dev/null +++ b/src/reducer.js @@ -0,0 +1,56 @@ +// Disabled due to consistency with other modules +/* eslint-disable default-param-last */ + +import { + parseData, + formatServerError, +} from '@openimis/fe-core'; +import { + ERROR, REQUEST, SUCCESS, +} from './util/action-type'; + +export const ACTION_TYPE = { + GET_DEDUPLICATION_SUMMARY: 'DEDUPLICATION_GET_DEDUPLICATION_SUMMARY', +}; + +function reducer( + state = { + submittingMutation: false, + mutation: {}, + fetchingSummary: false, + errorSummary: null, + fetchedSummary: false, + summary: [], + }, + action, +) { + switch (action.type) { + case REQUEST(ACTION_TYPE.GET_DEDUPLICATION_SUMMARY): + return { + ...state, + fetchingSummary: true, + fetchedSummary: false, + summary: null, + }; + case SUCCESS(ACTION_TYPE.GET_DEDUPLICATION_SUMMARY): + return { + ...state, + fetchingSummary: false, + fetchedSummary: true, + summary: action.payload.data.beneficiaryDeduplicationSummary.rows?.map((row) => ({ + ...row, + })), + errorSummary: null, + }; + case ERROR(ACTION_TYPE.GET_DEDUPLICATION_SUMMARY): + return { + ...state, + fetchingSummary: false, + errorSummary: formatServerError(action.payload), + }; + default: + return state; + } +} + +export default reducer; diff --git a/src/util/action-type.js b/src/util/action-type.js new file mode 100644 index 0000000..788e71a --- /dev/null +++ b/src/util/action-type.js @@ -0,0 +1,5 @@ +export const REQUEST = (actionTypeName) => `${actionTypeName}_REQ`; +export const SUCCESS = (actionTypeName) => `${actionTypeName}_RESP`; +export const ERROR = (actionTypeName) => `${actionTypeName}_ERR`; +export const CLEAR = (actionTypeName) => `${actionTypeName}_CLEAR`; +export const VALID = (actionTypeName) => `${actionTypeName}_VALID`; diff --git a/src/util/styles.js b/src/util/styles.js new file mode 100644 index 0000000..8ebced6 --- /dev/null +++ b/src/util/styles.js @@ -0,0 +1,24 @@ +export const defaultPageStyles = (theme) => ({ + page: theme.page, +}); + +export const defaultFilterStyles = (theme) => ({ + form: { + padding: 0, + }, + item: { + padding: theme.spacing(1), + }, +}); + +export const defaultHeadPanelStyles = (theme) => ({ + tableTitle: theme.table.title, + item: theme.paper.item, + fullHeight: { + height: '100%', + }, +}); + +export const defaultDialogStyles = (theme) => ({ + item: theme.paper.item, +}); From 3985916c79f7d86b735ad8d6159fb60c828f1816 Mon Sep 17 00:00:00 2001 From: sniedzielski Date: Tue, 16 Jan 2024 19:31:43 +0100 Subject: [PATCH 3/4] CM-454: added displaying columns group in more readible way --- src/components/tables/DeduplicationSummaryTable.js | 13 ++++++++++++- src/reducer.js | 1 - 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/tables/DeduplicationSummaryTable.js b/src/components/tables/DeduplicationSummaryTable.js index 97e2f24..458ebeb 100644 --- a/src/components/tables/DeduplicationSummaryTable.js +++ b/src/components/tables/DeduplicationSummaryTable.js @@ -42,6 +42,17 @@ function DeduplicationSummaryTable({ dispatch(fetchDeduplicationSummary(params)); }, []); + function reshapeColumnValues(inputString) { + const columnValues = JSON.parse(inputString); + const formattedValues = Object.entries(columnValues).map(([key, value]) => { + const formattedKey = key.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()); + const formattedValue = value !== null ? value : 'null'; + return `${formattedKey}: ${formattedValue}`; + }); + const resultString = formattedValues.join(', '); + return resultString; + } + return ( @@ -62,7 +73,7 @@ function DeduplicationSummaryTable({ {' '} - {result.columnValues} + {reshapeColumnValues(result.columnValues)} {' '} diff --git a/src/reducer.js b/src/reducer.js index f5d6010..5b5b602 100644 --- a/src/reducer.js +++ b/src/reducer.js @@ -2,7 +2,6 @@ /* eslint-disable default-param-last */ import { - parseData, formatServerError, } from '@openimis/fe-core'; import { From 9658f2138640a0e0d8924a4a5645545e41a768f0 Mon Sep 17 00:00:00 2001 From: sniedzielski Date: Tue, 16 Jan 2024 19:43:44 +0100 Subject: [PATCH 4/4] CM-454: empty state once reopen modal to trigger summary --- src/components/dialogs/DeduplicationFieldSelectionDialog.js | 2 ++ src/components/dialogs/DeduplicationSummaryDialog.js | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/dialogs/DeduplicationFieldSelectionDialog.js b/src/components/dialogs/DeduplicationFieldSelectionDialog.js index fb183b3..aedf531 100644 --- a/src/components/dialogs/DeduplicationFieldSelectionDialog.js +++ b/src/components/dialogs/DeduplicationFieldSelectionDialog.js @@ -31,6 +31,7 @@ function DeduplicationFieldSelectionDialog({ const [showSummaryDialog, setShowSummaryDialog] = useState(false); const handleOpen = () => { + setSelectedValues([]); setIsOpen(true); }; @@ -136,6 +137,7 @@ function DeduplicationFieldSelectionDialog({ handleClose={handleSummaryDialogClose} showSummaryDialog={showSummaryDialog} selectedValues={selectedValues} + setSelectedValues={setSelectedValues} /> )} diff --git a/src/components/dialogs/DeduplicationSummaryDialog.js b/src/components/dialogs/DeduplicationSummaryDialog.js index 6c87791..2f60f52 100644 --- a/src/components/dialogs/DeduplicationSummaryDialog.js +++ b/src/components/dialogs/DeduplicationSummaryDialog.js @@ -25,7 +25,6 @@ function DeduplicationSummaryDialog({ }) { if (!benefitPlan) return null; - // Extract the 'id' values from each object in the array const columns = selectedValues.map((value) => value.id); const columnParam = `columns: ${JSON.stringify(columns)}`;