Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OM-82: voucher searcher implementation, redux init #2

Merged
merged 3 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading