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`;