Skip to content

Commit

Permalink
Merge pull request #2 from openimis/feature/OM-82
Browse files Browse the repository at this point in the history
OM-82: voucher searcher implementation, redux init
  • Loading branch information
jdolkowski authored Nov 27, 2023
2 parents 8bbb4d4 + 977f26e commit fe6030e
Show file tree
Hide file tree
Showing 10 changed files with 453 additions and 7 deletions.
23 changes: 23 additions & 0 deletions src/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {
formatPageQueryWithCount,
graphql,
} from '@openimis/fe-core';
import { ACTION_TYPE } from './reducer';

const WORKER_VOUCHER_PROJECTION = (modulesManager) => [
'id',
'code',
'status',
'assignedDate',
'expiryDate',
'dateCreated',
'dateUpdated',
`insuree ${modulesManager.getProjection('insuree.InsureePicker.projection')}`,
`policyholder ${modulesManager.getProjection('policyHolder.PolicyHolderPicker.projection')}`,
];

export function fetchWorkerVouchers(modulesManager, params) {
const queryParams = [...params, 'isDeleted: false'];
const payload = formatPageQueryWithCount('workerVoucher', queryParams, WORKER_VOUCHER_PROJECTION(modulesManager));
return graphql(payload, ACTION_TYPE.SEARCH_WORKER_VOUCHERS);
}
140 changes: 140 additions & 0 deletions src/components/VoucherFilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import React from 'react';
import _debounce from 'lodash/debounce';

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

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

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

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

const debouncedOnChangeFilters = _debounce(onChangeFilters, DEFAULT_DEBOUNCE_TIME);

const filterValue = (filterName) => filters?.[filterName]?.value;
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={3} className={classes.item}>
<TextInput
module="workerVoucher"
label="workerVoucher.code"
value={filterTextFieldValue('code')}
onChange={onChangeStringFilter('code', CONTAINS_LOOKUP)}
/>
</Grid>
<Grid item xs={3} className={classes.item}>
<WorkerVoucherStatusPicker
value={filterValue('status')}
nullLabel={formatMessage('workerVoucher.placeholder.any')}
withLabel
withNull
onChange={(status) => onChangeFilters([
{
id: 'status',
status,
filter: `status: ${status}`,
},
])}
/>
</Grid>
<Grid item xs={3} className={classes.item}>
<PublishedComponent
pubRef="core.DatePicker"
module="workerVoucher"
label="workerVoucher.assignedDate"
value={filterValue('assignedDate')}
onChange={(assignedDate) => onChangeFilters([
{
id: 'assignedDate',
value: assignedDate,
filter: `assignedDate: "${assignedDate}"`,
},
])}
/>
</Grid>
<Grid item xs={3} className={classes.item}>
<PublishedComponent
pubRef="core.DatePicker"
module="workerVoucher"
label="workerVoucher.expiryDate"
value={filterValue('expiryDate')}
onChange={(expiryDate) => onChangeFilters([
{
id: 'expiryDate',
value: expiryDate,
filter: `expiryDate: "${expiryDate}"`,
},
])}
/>
</Grid>
<Grid item xs={3} className={classes.item}>
<TextInput
module="workerVoucher"
label="workerVoucher.employer.code"
value={filterTextFieldValue('policyholder_Code')}
onChange={onChangeStringFilter('policyholder_Code', CONTAINS_LOOKUP)}
/>
</Grid>
<Grid item xs={3} className={classes.item}>
<TextInput
module="workerVoucher"
label="workerVoucher.employer.tradename"
value={filterTextFieldValue('policyholder_TradeName')}
onChange={onChangeStringFilter('policyholder_TradeName', CONTAINS_LOOKUP)}
/>
</Grid>
<Grid item xs={3} className={classes.item}>
<TextInput
module="workerVoucher"
label="workerVoucher.worker.code"
value={filterTextFieldValue('insuree_ChfId')}
onChange={onChangeStringFilter('insuree_ChfId', CONTAINS_LOOKUP)}
/>
</Grid>
<Grid item xs={3} className={classes.item}>
<TextInput
module="workerVoucher"
label="workerVoucher.worker.lastName"
value={filterTextFieldValue('insuree_LastName')}
onChange={onChangeStringFilter('insuree_LastName', CONTAINS_LOOKUP)}
/>
</Grid>
</Grid>
);
}

export default VoucherFilter;
108 changes: 108 additions & 0 deletions src/components/VoucherSearcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';

import { IconButton, Tooltip } from '@material-ui/core';
import VisibilityIcon from '@material-ui/icons/Visibility';

import {
Searcher, useHistory, useModulesManager, useTranslations,
} from '@openimis/fe-core';
import { fetchWorkerVouchers } from '../actions';
import {
DEFAULT_PAGE_SIZE, MODULE_NAME, REF_ROUTE_WORKER_VOUCHER, ROWS_PER_PAGE_OPTIONS, VOUCHER_RIGHT_SEARCH,
} from '../constants';
import VoucherFilter from './VoucherFilter';

function VoucherSearcher() {
const history = useHistory();
const dispatch = useDispatch();
const modulesManager = useModulesManager();
const rights = useSelector((state) => state.core?.user?.i_user?.rights ?? []);
const { formatMessage, formatMessageWithValues } = useTranslations(MODULE_NAME, modulesManager);
const {
fetchingWorkerVouchers,
fetchedWorkerVouchers,
errorWorkerVouchers,
workerVouchers,
workerVouchersPageInfo,
workerVouchersTotalCount,
} = useSelector((state) => state.workerVoucher);

const fetchVouchers = (params) => {
try {
dispatch(fetchWorkerVouchers(modulesManager, params));
} catch (error) {
throw new Error(`[VOUCHER_SEARCHER]: Fetching vouchers failed. ${error}`);
}
};

const headers = () => [
'workerVoucher.code',
'workerVoucher.employer',
'workerVoucher.worker',
'workerVoucher.status',
'workerVoucher.assignedDate',
'workerVoucher.expiryDate',
'emptyLabel',
];

const sorts = () => [
['code', true],
['policyholder', true],
['insuree', true],
['status', true],
['assignedDate', true],
['expiryDate', true],
];

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

const openWorkerVoucher = (workerVoucher) => rights.includes(VOUCHER_RIGHT_SEARCH) && history.push(
`/${modulesManager.getRef(REF_ROUTE_WORKER_VOUCHER)}/${workerVoucher?.uuid}}`,
);

const onDoubleClick = (workerVoucher) => openWorkerVoucher(workerVoucher);

const itemFormatters = () => [
(workerVoucher) => workerVoucher.code,
(workerVoucher) => `${workerVoucher.policyholder.code} ${workerVoucher.policyholder.tradeName}`,
(workerVoucher) => `${workerVoucher.insuree.chfId} ${workerVoucher.insuree.lastName}`,
(workerVoucher) => workerVoucher.status,
(workerVoucher) => workerVoucher.assignedDate,
(workerVoucher) => workerVoucher.expiryDate,
(workerVoucher) => (
<Tooltip title={formatMessage('workerVoucher.tooltip.details')}>
<IconButton onClick={() => openWorkerVoucher(workerVoucher)}>
<VisibilityIcon />
</IconButton>
</Tooltip>
),
];

const voucherFilter = ({ filters, onChangeFilters }) => (
<VoucherFilter filters={filters} onChangeFilters={onChangeFilters} formatMessage={formatMessage} />
);

return (
<Searcher
module="workerVoucher"
FilterPane={voucherFilter}
fetch={fetchVouchers}
items={workerVouchers}
itemsPageInfo={workerVouchersPageInfo}
fetchedItems={fetchedWorkerVouchers}
fetchingItems={fetchingWorkerVouchers}
errorItems={errorWorkerVouchers}
tableTitle={formatMessageWithValues('workerVoucher.searcherResultsTitle', { workerVouchersTotalCount })}
headers={headers}
itemFormatters={itemFormatters}
sorts={sorts}
rowsPerPageOptions={ROWS_PER_PAGE_OPTIONS}
defaultPageSize={DEFAULT_PAGE_SIZE}
rowIdentifier={rowIdentifier}
onDoubleClick={onDoubleClick}
/>
);
}

export default VoucherSearcher;
30 changes: 28 additions & 2 deletions src/constants.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,28 @@
// TODO: Adjust rights to the proper ones after BE implementation
export const VOUCHER_RIGHT_SEARCH = 101101;
export const VOUCHER_RIGHT_SEARCH = 204001;
export const MODULE_NAME = 'workerVoucher';

export const REF_ROUTE_WORKER_VOUCHER = 'workerVoucher.route.workerVoucher';

export const DEFAULT_DEBOUNCE_TIME = 800;
export const DEFAULT_PAGE_SIZE = 10;
export const ROWS_PER_PAGE_OPTIONS = [10, 20, 50, 100];
export const EMPTY_STRING = '';
export const CONTAINS_LOOKUP = 'Icontains';

const WORKER_VOUCHER_STATUS = {
AWAITING_PAYMENT: 'AWAITING_PAYMENT',
UNASSIGNED: 'UNASSIGNED',
ASSIGNED: 'ASSIGNED',
EXPIRED: 'EXPIRED',
CANCELED: 'CANCELED',
CLOSED: 'CLOSED',
};

export const WORKER_VOUCHER_STATUS_LIST = [
WORKER_VOUCHER_STATUS.AWAITING_PAYMENT,
WORKER_VOUCHER_STATUS.UNASSIGNED,
WORKER_VOUCHER_STATUS.ASSIGNED,
WORKER_VOUCHER_STATUS.EXPIRED,
WORKER_VOUCHER_STATUS.CANCELED,
WORKER_VOUCHER_STATUS.CLOSED,
];
10 changes: 9 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@

import React from 'react';

import GroupAddIcon from '@material-ui/icons/GroupAdd';
import ListAltIcon from '@material-ui/icons/ListAlt';
import LocalAtmIcon from '@material-ui/icons/LocalAtm';
import GroupAddIcon from '@material-ui/icons/GroupAdd';

import { FormattedMessage } from '@openimis/fe-core';
import { VOUCHER_RIGHT_SEARCH } from './constants';
import VoucherAcquirementPage from './pages/VoucherAcquirementPage';
import VoucherAssignmentPage from './pages/VoucherAssignmentPage';
import VoucherDetailsPage from './pages/VoucherDetailsPage';
import VouchersPage from './pages/VouchersPage';
import WorkerVoucherStatusPicker from './pickers/WorkerVoucherStatusPicker';
import reducer from './reducer';
import messages_en from './translations/en.json';

const ROUTE_WORKER_VOUCHERS_LIST = 'voucher/vouchers';
Expand All @@ -23,6 +25,12 @@ const ROUTE_WORKER_VOUCHER_ASSIGNMENT = 'voucher/assignment';

const DEFAULT_CONFIG = {
translations: [{ key: 'en', messages: messages_en }],
reducers: [{ key: 'workerVoucher', reducer }],
refs: [
{ key: 'workerVoucher.route.workerVouchers', ref: ROUTE_WORKER_VOUCHERS_LIST },
{ key: 'workerVoucher.route.workerVoucher', ref: ROUTE_WORKER_VOUCHER },
{ key: 'workerVoucher.WorkerVoucherStatusPicker', ref: WorkerVoucherStatusPicker },
],
'worker.MainMenu': [
{
text: <FormattedMessage module="workerVoucher" id="menu.voucherList" />,
Expand Down
25 changes: 22 additions & 3 deletions src/pages/VouchersPage.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
import React from 'react';
import { useSelector } from 'react-redux';

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

import { Helmet, useModulesManager, useTranslations } from '@openimis/fe-core';
import VoucherSearcher from '../components/VoucherSearcher';
import { MODULE_NAME, VOUCHER_RIGHT_SEARCH } from '../constants';

export const useStyles = makeStyles((theme) => ({
page: theme.page,
}));

function VouchersPage() {
const modulesManager = useModulesManager();
const classes = useStyles();
const { formatMessage } = useTranslations(MODULE_NAME, modulesManager);
const rights = useSelector((state) => (state.core?.user?.i_user?.rights ?? []));

return (
<div>
VouchersPage
</div>
rights.includes(VOUCHER_RIGHT_SEARCH) && (
<div className={classes.page}>
<Helmet title={formatMessage('workerVoucher.vouchers')} />
<VoucherSearcher />
</div>
)
);
}

Expand Down
Loading

0 comments on commit fe6030e

Please sign in to comment.