Skip to content

Commit

Permalink
OM-342: groups init
Browse files Browse the repository at this point in the history
  • Loading branch information
olewandowski1 committed Oct 14, 2024
1 parent 659ff2b commit f6593f0
Show file tree
Hide file tree
Showing 11 changed files with 907 additions and 1 deletion.
37 changes: 37 additions & 0 deletions src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ const WORKER_PROJECTION = (modulesManager) => [
'photo { photo }',
];

// TODO: Adjust the group projection after BE changes
// eslint-disable-next-line no-unused-vars
export const GROUP_PROJECTION = (modulesManager) => [
'id',
'uuid',
'otherNames',
'lastName',
// `workers {${WORKER_PROJECTION(modulesManager)}}`,
];

function formatGraphQLDateRanges(dateRanges) {
const rangeStrings = dateRanges.map((range) => `{ startDate: "${range.startDate}", endDate: "${range.endDate}" }`);
return `[${rangeStrings.join(', ')}]`;
Expand Down Expand Up @@ -533,3 +543,30 @@ export function validateMConnectWorker(nationalId, economicUnitCode) {
{ nationalId, economicUnitCode },
);
}

export function fetchGroupsAction(modulesManager, params) {
const queryParams = [...params];
// TODO: Change to `group` after BE changes
const payload = formatPageQueryWithCount('worker', queryParams, GROUP_PROJECTION(modulesManager));
return graphql(payload, ACTION_TYPE.GET_GROUPS);
}

export function fetchGroup(modulesManager, params) {
const queryParams = [...params];
// TODO: Change to `group` after BE changes
const payload = formatPageQueryWithCount('worker', queryParams, GROUP_PROJECTION(modulesManager));
return graphql(payload, ACTION_TYPE.GET_GROUP);
}

export const clearGroup = () => (dispatch) => {
dispatch({
type: CLEAR(ACTION_TYPE.GET_GROUP),
});
};

// TODO: Adjust the group mutation after BE changes
export function createGroup() {}

export function updateGroup() {}

export function deleteGroup() {}
61 changes: 61 additions & 0 deletions src/components/groups/GroupFilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';
import _debounce from 'lodash/debounce';

import { Grid } from '@material-ui/core';
import { makeStyles } from '@material-ui/styles';

import { TextInput } from '@openimis/fe-core';
import { CONTAINS_LOOKUP, DEFAULT_DEBOUNCE_TIME, EMPTY_STRING } from '../../constants';

export const useStyles = makeStyles((theme) => ({
form: {
padding: '0 0 10px 0',
width: '100%',
},
item: {
padding: theme.spacing(1),
},
}));

function GroupFilter({ filters, onChangeFilters }) {
const classes = useStyles();

const debouncedOnChangeFilters = _debounce(onChangeFilters, DEFAULT_DEBOUNCE_TIME);

const filterTextFieldValue = (filterName) => filters?.[filterName]?.value ?? EMPTY_STRING;

const onChangeStringFilter = (filterName, lookup = null) => (value) => {
if (lookup) {
debouncedOnChangeFilters([
{
id: filterName,
value,
filter: `${filterName}_${lookup}: "${value}"`,
},
]);
} else {
onChangeFilters([
{
id: filterName,
value,
filter: `${filterName}: "${value}"`,
},
]);
}
};

return (
<Grid container className={classes.form}>
<Grid item xs={4} className={classes.item}>
<TextInput
module="workerVoucher"
label="workerVoucher.group.name"
value={filterTextFieldValue('groupName')}
onChange={onChangeStringFilter('groupName', CONTAINS_LOOKUP)}
/>
</Grid>
</Grid>
);
}

export default GroupFilter;
66 changes: 66 additions & 0 deletions src/components/groups/GroupMasterPanel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from 'react';

import {
Divider,
Grid,
Paper,
Typography,
} from '@material-ui/core';
import { withStyles, withTheme } from '@material-ui/core/styles';

import {
FormattedMessage, FormPanel, TextInput, withModulesManager,
} from '@openimis/fe-core';
import { EMPTY_STRING } from '../../constants';
import GroupWorkerManagePanel from './GroupWorkerManagePanel';

const styles = (theme) => ({
paper: theme.paper.paper,
tableTitle: theme.table.title,
item: theme.paper.item,
});

class GroupMasterPanel extends FormPanel {
render() {
const {
classes, edited, readOnly, onEditedChanged,
} = this.props;

return (
<Grid item xs={12}>
<Paper className={classes.paper}>
<Grid container className={classes.tableTitle}>
<Grid item xs={3} container alignItems="center" className={classes.item}>
<Typography variant="h5">
<FormattedMessage
module="workerVoucher"
id={edited?.uuid ? 'group.edit' : 'group.new'}
values={{
name: edited?.name ?? EMPTY_STRING,
}}
/>
</Typography>
</Grid>
</Grid>
<Divider />
<Grid item xs={4} className={classes.item}>
<TextInput
module="workerVoucher"
label="group.name"
required
readOnly={readOnly}
value={edited?.name ?? EMPTY_STRING}
onChange={(v) => this.updateAttribute('name', v)}
/>
</Grid>
<Divider />
<Grid container>
<GroupWorkerManagePanel edited={edited} onChange={onEditedChanged} />
</Grid>
</Paper>
</Grid>
);
}
}

export default withModulesManager(withTheme(withStyles(styles)(GroupMasterPanel)));
213 changes: 213 additions & 0 deletions src/components/groups/GroupSearcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import React, {
useCallback, useEffect, useRef, useState,
} from 'react';
import { useDispatch, useSelector } from 'react-redux';

import { IconButton, Tooltip } from '@material-ui/core';
import DeleteIcon from '@material-ui/icons/Delete';
import EditIcon from '@material-ui/icons/Edit';

import {
Searcher, SelectDialog, journalize, useHistory, useModulesManager, useTranslations,
} from '@openimis/fe-core';
import { deleteGroup, fetchGroupsAction } from '../../actions';
import {
DEFAULT_PAGE_SIZE,
MODULE_NAME,
RIGHT_GROUP_DELETE,
RIGHT_GROUP_EDIT,
RIGHT_GROUP_SEARCH,
ROWS_PER_PAGE_OPTIONS,
} from '../../constants';
import GroupFilter from './GroupFilter';

function GroupSearcher() {
const history = useHistory();
const dispatch = useDispatch();
const modulesManager = useModulesManager();
const prevSubmittingMutationRef = useRef();

const rights = useSelector((state) => state.core?.user?.i_user?.rights ?? []);
const {
fetchingGroups,
fetchedGroups,
errorGroups,
groups,
// TODO: Uncomment when BE is ready
// groupsPageInfo,
// groupsTotalCount,
mutation,
submittingMutation,
} = useSelector((state) => state.workerVoucher);
const { economicUnit } = useSelector((state) => state.policyHolder);

const { formatMessage, formatMessageWithValues } = useTranslations(MODULE_NAME, modulesManager);

const [queryParams, setQueryParams] = useState([]);
const [deleteGroupDialogOpen, setDeleteGroupDialogOpen] = useState(false);
const [groupToDelete, setGroupToDelete] = useState(null);

const fetchGroups = useCallback(
(params) => {
try {
const actionParams = [...params];

if (economicUnit?.code) {
actionParams.push(`economicUnitCode:"${economicUnit.code}"`);
}

dispatch(fetchGroupsAction(modulesManager, actionParams));
} catch (error) {
throw new Error(`[GROUP_SEARCHER]: Fetching groups failed. ${error}`);
}
},
[economicUnit],
);

const headers = () => [
'workerVoucher.GroupSearcher.groupName',
'workerVoucher.GroupSearcher.dateCreated',
'emptyLabel',
];

const sorts = () => [
['groupName', true],
['dateCreated', true],
];

const rowIdentifier = (group) => group.uuid;

const openGroup = (group) => rights.includes(RIGHT_GROUP_SEARCH)
&& history.push(`/${modulesManager.getRef('workerVoucher.route.group')}/${group?.uuid}`);

const onDoubleClick = (group) => openGroup(group);

const onDeleteGroupDialogOpen = (group) => {
setDeleteGroupDialogOpen((prevState) => !prevState);
setGroupToDelete(group);
};

const onDeleteGroupDialogClose = () => {
setDeleteGroupDialogOpen((prevState) => !prevState);
setGroupToDelete(null);
};

const onDeleteGroupConfirm = () => {
try {
dispatch(deleteGroup(economicUnit, groupToDelete, 'Delete Group'));
fetchGroups(queryParams);
} catch (error) {
throw new Error(`[GROUP_SEARCHER]: Deletion failed. ${error}`);
} finally {
setDeleteGroupDialogOpen((prevState) => !prevState);
}
};

const itemFormatters = () => [
(group) => group.chfId,
(group) => group.lastName,
(group) => group.otherNames,
(group) => (
<div style={{ textAlign: 'right' }}>
{rights.includes(RIGHT_GROUP_EDIT) && (
<Tooltip title={formatMessage('workerVoucher.tooltip.edit')}>
<IconButton onClick={() => openGroup(group)}>
<EditIcon />
</IconButton>
</Tooltip>
)}
{rights.includes(RIGHT_GROUP_DELETE) && (
<Tooltip title={formatMessage('workerVoucher.tooltip.delete')}>
<IconButton onClick={() => onDeleteGroupDialogOpen(group)}>
<DeleteIcon />
</IconButton>
</Tooltip>
)}
</div>
),
];

const groupFilters = ({ filters, onChangeFilters }) => (
<GroupFilter filters={filters} onChangeFilters={onChangeFilters} />
);

const filtersToQueryParams = ({
filters, pageSize, beforeCursor, afterCursor, orderBy,
}) => {
const queryParams = Object.keys(filters)
.filter((f) => !!filters[f].filter)
.map((f) => filters[f].filter);
if (!beforeCursor && !afterCursor) {
queryParams.push(`first: ${pageSize}`);
}
if (afterCursor) {
queryParams.push(`after: "${afterCursor}"`);
queryParams.push(`first: ${pageSize}`);
}
if (beforeCursor) {
queryParams.push(`before: "${beforeCursor}"`);
queryParams.push(`last: ${pageSize}`);
}
if (orderBy) {
queryParams.push(`orderBy: ["${orderBy}"]`);
}
setQueryParams(queryParams);
return queryParams;
};

useEffect(() => {
if (queryParams.length) {
fetchGroups(queryParams);
}
}, [economicUnit, queryParams]);

useEffect(() => {
if (prevSubmittingMutationRef.current && !submittingMutation) {
dispatch(journalize(mutation));
}
}, [submittingMutation]);

useEffect(() => {
prevSubmittingMutationRef.current = submittingMutation;
});

return (
<>
<Searcher
module="workerVoucher"
FilterPane={groupFilters}
fetch={fetchGroups}
items={groups}
// TODO: Uncomment when BE is ready
// itemsPageInfo={groupsPageInfo}
itemsPageInfo={{ totalCount: 0 }}
fetchedItems={fetchedGroups}
fetchingItems={fetchingGroups}
errorItems={errorGroups}
filtersToQueryParams={filtersToQueryParams}
// TODO: Uncomment when BE is ready
// tableTitle={formatMessageWithValues('workerVoucher.GroupSearcher.resultsTitle', { groupsTotalCount })}
tableTitle={formatMessageWithValues('workerVoucher.GroupSearcher.resultsTitle', { groupsTotalCount: 0 })}
headers={headers}
itemFormatters={itemFormatters}
sorts={sorts}
rowsPerPageOptions={ROWS_PER_PAGE_OPTIONS}
defaultPageSize={DEFAULT_PAGE_SIZE}
rowIdentifier={rowIdentifier}
onDoubleClick={onDoubleClick}
/>
<SelectDialog
confirmState={deleteGroupDialogOpen}
onConfirm={() => onDeleteGroupConfirm()}
onClose={() => onDeleteGroupDialogClose()}
module="workerVoucher"
confirmTitle="GroupSearcher.dialog.title"
confirmMessage="GroupSearcher.dialog.message"
confirmationButton="GroupSearcher.dialog.confirm"
rejectionButton="GroupSearcher.dialog.abandon"
/>
</>
);
}

export default GroupSearcher;
Loading

0 comments on commit f6593f0

Please sign in to comment.