diff --git a/src/actions.js b/src/actions.js
new file mode 100644
index 0000000..bd0ae8a
--- /dev/null
+++ b/src/actions.js
@@ -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);
+}
diff --git a/src/components/VoucherFilter.js b/src/components/VoucherFilter.js
new file mode 100644
index 0000000..3feb023
--- /dev/null
+++ b/src/components/VoucherFilter.js
@@ -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 (
+
+
+
+
+
+ onChangeFilters([
+ {
+ id: 'status',
+ status,
+ filter: `status: ${status}`,
+ },
+ ])}
+ />
+
+
+ onChangeFilters([
+ {
+ id: 'assignedDate',
+ value: assignedDate,
+ filter: `assignedDate: "${assignedDate}"`,
+ },
+ ])}
+ />
+
+
+ onChangeFilters([
+ {
+ id: 'expiryDate',
+ value: expiryDate,
+ filter: `expiryDate: "${expiryDate}"`,
+ },
+ ])}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default VoucherFilter;
diff --git a/src/components/VoucherSearcher.js b/src/components/VoucherSearcher.js
new file mode 100644
index 0000000..9e2acf1
--- /dev/null
+++ b/src/components/VoucherSearcher.js
@@ -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) => (
+
+ openWorkerVoucher(workerVoucher)}>
+
+
+
+ ),
+ ];
+
+ const voucherFilter = ({ filters, onChangeFilters }) => (
+
+ );
+
+ return (
+
+ );
+}
+
+export default VoucherSearcher;
diff --git a/src/constants.js b/src/constants.js
index dcba42d..5c16078 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -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,
+];
diff --git a/src/index.js b/src/index.js
index 21f4122..1bc5d9d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -4,9 +4,9 @@
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';
@@ -14,6 +14,8 @@ 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';
@@ -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: ,
diff --git a/src/pages/VouchersPage.js b/src/pages/VouchersPage.js
index 7744413..df17bf4 100644
--- a/src/pages/VouchersPage.js
+++ b/src/pages/VouchersPage.js
@@ -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 (
-
- VouchersPage
-
+ rights.includes(VOUCHER_RIGHT_SEARCH) && (
+
+
+
+
+ )
);
}
diff --git a/src/pickers/WorkerVoucherStatusPicker.js b/src/pickers/WorkerVoucherStatusPicker.js
new file mode 100644
index 0000000..639bc00
--- /dev/null
+++ b/src/pickers/WorkerVoucherStatusPicker.js
@@ -0,0 +1,31 @@
+import React from 'react';
+
+import { ConstantBasedPicker } from '@openimis/fe-core';
+import { WORKER_VOUCHER_STATUS_LIST } from '../constants';
+
+function WorkerVoucherStatusPicker({
+ required = false,
+ withNull = false,
+ readOnly = false,
+ nullLabel = null,
+ withLabel = false,
+ value,
+ onChange,
+}) {
+ return (
+
+ );
+}
+
+export default WorkerVoucherStatusPicker;
diff --git a/src/reducer.js b/src/reducer.js
new file mode 100644
index 0000000..cfc0c82
--- /dev/null
+++ b/src/reducer.js
@@ -0,0 +1,66 @@
+/* eslint-disable default-param-last */
+
+import {
+ decodeId,
+ formatGraphQLError,
+ formatServerError,
+ pageInfo,
+ parseData,
+} from '@openimis/fe-core';
+import {
+ ERROR, REQUEST, SUCCESS,
+} from './utils/action-type';
+
+export const ACTION_TYPE = {
+ SEARCH_WORKER_VOUCHERS: 'WORKER_VOUCHER_WORKER_VOUCHERS',
+};
+
+const STORE_STATE = {
+ fetchingWorkerVouchers: false,
+ fetchedWorkerVouchers: false,
+ errorWorkerVouchers: null,
+ workerVouchers: [],
+ workerVouchersPageInfo: {},
+ workerVouchersTotalCount: 0,
+};
+
+function reducer(
+ state = STORE_STATE,
+ action,
+) {
+ switch (action.type) {
+ case REQUEST(ACTION_TYPE.SEARCH_WORKER_VOUCHERS):
+ return {
+ ...state,
+ fetchingWorkerVouchers: true,
+ fetchedWorkerVouchers: false,
+ errorWorkerVouchers: null,
+ workerVouchers: [],
+ workerVouchersPageInfo: {},
+ workerVouchersTotalCount: 0,
+ };
+ case SUCCESS(ACTION_TYPE.SEARCH_WORKER_VOUCHERS):
+ return {
+ ...state,
+ fetchingWorkerVouchers: false,
+ fetchedWorkerVouchers: true,
+ errorWorkerVouchers: formatGraphQLError(action.payload),
+ workerVouchers: parseData(action.payload.data.workerVoucher)?.map((voucher) => ({
+ ...voucher,
+ uuid: decodeId(voucher.id),
+ })),
+ workerVouchersPageInfo: pageInfo(action.payload.data.workerVoucher),
+ workerVouchersTotalCount: action.payload.data.workerVoucher?.totalCount ?? 0,
+ };
+ case ERROR(ACTION_TYPE.SEARCH_WORKER_VOUCHERS):
+ return {
+ ...state,
+ fetchingWorkerVouchers: false,
+ errorWorkerVouchers: formatServerError(action.payload),
+ };
+ default:
+ return state;
+ }
+}
+
+export default reducer;
diff --git a/src/translations/en.json b/src/translations/en.json
index 2837360..43a3082 100644
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -1,5 +1,25 @@
{
"workerVoucher.menu.voucherList": "Voucher List",
"workerVoucher.menu.voucherAcquirement": "Voucher Acquirement",
- "workerVoucher.menu.voucherAssignment": "Voucher Assignment"
+ "workerVoucher.menu.voucherAssignment": "Voucher Assignment",
+ "workerVoucher.vouchers": "Vouchers",
+ "workerVoucher.searcherResultsTitle": "{workerVouchersTotalCount} Vouchers Found",
+ "workerVoucher.code": "Code",
+ "workerVoucher.employer": "Employer",
+ "workerVoucher.employer.tradename": "Employer Trade Name",
+ "workerVoucher.employer.code": "Employer Code",
+ "workerVoucher.worker": "Worker",
+ "workerVoucher.worker.code": "Worker National ID",
+ "workerVoucher.worker.lastName": "Worker Last Name",
+ "workerVoucher.status": "Status",
+ "workerVoucher.assignedDate": "Assigned Date",
+ "workerVoucher.expiryDate": "Expiry Date",
+ "workerVoucher.tooltip.details": "View details",
+ "workerVoucher.placeholder.any": "Any",
+ "workerVoucher.status.AWAITING_PAYMENT": "AWAITING PAYMENT",
+ "workerVoucher.status.UNASSIGNED": "UNASSIGNED",
+ "workerVoucher.status.ASSIGNED": "ASSIGNED",
+ "workerVoucher.status.EXPIRED": "EXPIRED",
+ "workerVoucher.status.CANCELED": "CANCELED",
+ "workerVoucher.status.CLOSED": "CLOSED"
}
diff --git a/src/utils/action-type.js b/src/utils/action-type.js
new file mode 100644
index 0000000..788e71a
--- /dev/null
+++ b/src/utils/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`;