diff --git a/src/components/TaskAllFilter.js b/src/components/TaskAllFilter.js new file mode 100644 index 0000000..70b5152 --- /dev/null +++ b/src/components/TaskAllFilter.js @@ -0,0 +1,146 @@ +import React from 'react'; +import { injectIntl } from 'react-intl'; +import { Grid } from '@material-ui/core'; +import { withTheme, withStyles } from '@material-ui/core/styles'; +import _debounce from 'lodash/debounce'; +import { + TextInput, PublishedComponent, formatMessage, decodeId, toISODateTime, +} from '@openimis/fe-core'; +import { defaultFilterStyles } from '../utils/styles'; +import { + CONTAINS_LOOKUP, DEFAULT_DEBOUNCE_TIME, EMPTY_STRING, MODULE_NAME, +} from '../constants'; + +function TaskAllFilter({ + intl, classes, filters, onChangeFilters, +}) { + 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: 'source', + value, + filter: value ? `source: "${value}"` : EMPTY_STRING, + }, + ])} + /> + + + + + + + + + onChangeFilters([ + { + id: 'taskGroupId', + value, + filter: value?.id ? `taskGroupId: "${decodeId(value.id)}"` : '', + }, + ])} + /> + + + onChangeFilters([ + { + id: 'status', + value, + filter: value ? `status: ${value}` : EMPTY_STRING, + }, + ])} + /> + + + onChangeFilters([ + { + id: 'dateCreated_Gte', + value: v, + filter: `dateCreated_Gte: "${toISODateTime(v)}"`, + }, + ])} + /> + + + onChangeFilters([ + { + id: 'dateCreated_Lte', + value: v, + filter: `dateCreated_Lte: "${toISODateTime(v)}"`, + }, + ])} + /> + + + ); +} + +export default injectIntl(withTheme(withStyles(defaultFilterStyles)(TaskAllFilter))); diff --git a/src/components/TaskAllSearcher.js b/src/components/TaskAllSearcher.js new file mode 100644 index 0000000..0e4250f --- /dev/null +++ b/src/components/TaskAllSearcher.js @@ -0,0 +1,142 @@ +import React from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { + Searcher, + useHistory, + historyPush, + useModulesManager, + useTranslations, +} from '@openimis/fe-core'; +import { IconButton, Tooltip } from '@material-ui/core'; +import VisibilityIcon from '@material-ui/icons/Visibility'; +import { + RIGHT_TASKS_MANAGEMENT_SEARCH, DEFAULT_PAGE_SIZE, ROWS_PER_PAGE_OPTIONS, TASK_STATUS, TASK_ROUTE, +} from '../constants'; +import TaskAllFilter from './TaskAllFilter'; +import { fetchTasks } from '../actions'; +import trimBusinessEvent from '../utils/trimBusinessEvent'; + +function TaskAllSearcher({ + rights, showFilters = true, +}) { + const history = useHistory(); + const modulesManager = useModulesManager(); + const dispatch = useDispatch(); + const { + formatMessage, + formatMessageWithValues, + formatDateTimeFromISO, + } = useTranslations('tasksManagement', modulesManager); + + const fetchingTasks = useSelector((state) => state?.tasksManagement?.fetchingTasks); + const fetchedTasks = useSelector((state) => state?.tasksManagement?.fetchedTasks); + const errorTasks = useSelector((state) => state?.tasksManagement?.errorTasks); + const tasks = useSelector((state) => state?.tasksManagement?.tasks); + const tasksPageInfo = useSelector((state) => state?.tasksManagement?.tasksPageInfo); + const tasksTotalCount = useSelector((state) => state?.tasksManagement?.tasksTotalCount); + + const openTask = (task, newTab = false) => historyPush( + modulesManager, + history, + TASK_ROUTE, + [task?.id], + newTab, + ); + + const onDoubleClick = (task) => openTask(task); + const fetch = (params) => dispatch(fetchTasks(modulesManager, params)); + + const rowIdentifier = (task) => task.id; + + const isRowDisabled = (_, task) => task.status !== TASK_STATUS.ACCEPTED; + + const headers = () => { + const headers = [ + 'task.source', + 'task.type', + 'task.entity', + 'task.assignee', + 'task.dateCreated', + 'task.status', + ]; + if (rights.includes(RIGHT_TASKS_MANAGEMENT_SEARCH)) { + headers.push('emptyLabel'); + } + return headers; + }; + + const sorts = () => [ + ['source', true], + ['type', true], + ['entity', true], + ['assignee', true], + ['date_created', true], + ['status', true], + ]; + + const itemFormatters = () => [ + (task) => task.source, + (task) => trimBusinessEvent(task.businessEvent), + (task) => task.entityString, + (task) => task?.taskGroup?.code, + (task) => formatDateTimeFromISO(task?.dateCreated), + (task) => task.status, + (task) => ( + + openTask(task)} + > + + + + ), + ]; + + const defaultFilters = () => { + const filters = { + isDeleted: { + value: false, + filter: 'isDeleted: false', + }, + }; + return filters; + }; + + const taskFilter = (props) => ( + + ); + + return ( + + ); +} + +export default TaskAllSearcher; diff --git a/src/constants.js b/src/constants.js index 25cf369..50f72b0 100644 --- a/src/constants.js +++ b/src/constants.js @@ -45,6 +45,7 @@ export const TASK_STATUS_LIST = [ export const GROUP_RESOLVE_POLICY_LIST = [GROUP_RESOLVE_POLICY.ALL, GROUP_RESOLVE_POLICY.ANY, GROUP_RESOLVE_POLICY.N]; export const RIGHT_TASKS_MANAGEMENT_SEARCH = 191001; +export const RIGHT_TASKS_MANAGEMENT_SEARCH_ALL = 191005; export const TASKS_MANAGEMENT_ROUTE_GROUPS_GROUP = 'tasksManagement.route.group'; @@ -65,3 +66,29 @@ export const FAILED = 'FAILED'; export const DOT = '.'; export const TASK_ROUTE = 'tasksManagement.route.task'; + +export const TASK_AVAILABLE_SOURCES = [ + 'IndividualService', + 'GroupIndividualService', + 'CreateGroupAndMoveIndividualService', + 'BenefitPlanService', + 'BeneficiaryService', + 'calcrule_social_protection', + 'import_valid_items', + 'payroll', + 'payroll_reconciliation', + 'payroll_reject', + 'payroll_delete', + 'CreateDeduplicationReviewTasksService', +]; + +export const TASK_AVAILABLE_TYPES = [ + 'create', + 'update', + 'calculate', + 'import_valid_items', + 'accept_payroll', + 'payroll_reconciliation', + 'payroll_reject', + 'payroll_delete', +]; diff --git a/src/index.js b/src/index.js index 6b04f6f..24464e3 100644 --- a/src/index.js +++ b/src/index.js @@ -16,9 +16,13 @@ import TaskSearcher from './components/TaskSearcher'; import getAdminMainMenuContributions from './contributions/AdminMainMenuContributions'; import { TASK_ROUTE } from './constants'; import { fetchTask, resolveTask } from './actions'; +import TasksAllPage from './pages/TasksAllPage'; +import TaskTypesPicker from './pickers/TaskTypesPicker'; +import TaskSourcesPicker from './pickers/TaskSourcesPicker'; const ROUTE_TASKS_MANAGEMENT = 'tasks'; const ROUTE_TASK_MANAGEMENT = 'tasks/task'; +const ROUTE_TASKS_ALL_MANAGEMENT = 'allTasks'; const ROUTE_GROUPS_MANAGEMENT = 'tasks/groups'; const ROUTE_GROUP_MANAGEMENT = 'tasks/groups/group'; @@ -30,6 +34,7 @@ const DEFAULT_CONFIG = { 'admin.MainMenu': [...getAdminMainMenuContributions()], 'core.Router': [ { path: ROUTE_TASKS_MANAGEMENT, component: TasksManagementPage }, + { path: ROUTE_TASKS_ALL_MANAGEMENT, component: TasksAllPage }, { path: `${ROUTE_TASK_MANAGEMENT}/:task_uuid?`, component: TaskDetailsPage }, { path: ROUTE_GROUPS_MANAGEMENT, component: GroupsManagementPage }, { path: `${ROUTE_GROUP_MANAGEMENT}/:task_group_uuid?`, component: TaskGroupPage }, @@ -38,6 +43,8 @@ const DEFAULT_CONFIG = { { key: TASK_ROUTE, ref: ROUTE_TASK_MANAGEMENT }, { key: 'tasksManagement.route.group', ref: ROUTE_GROUP_MANAGEMENT }, { key: 'tasksManagement.taskStatusPicker', ref: TaskStatusPicker }, + { key: 'tasksManagement.taskTypesPicker', ref: TaskTypesPicker }, + { key: 'tasksManagement.taskSourcesPicker', ref: TaskSourcesPicker }, { key: 'tasksManagement.taskPreviewCell', ref: TaskPreviewCell }, { key: 'tasksManagement.taskGroupPicker', ref: TaskGroupPicker }, { key: 'tasksManagement.taskSearcher', ref: TaskSearcher }, diff --git a/src/menus/TasksMainMenu.js b/src/menus/TasksMainMenu.js index 1823fcc..737c5e1 100644 --- a/src/menus/TasksMainMenu.js +++ b/src/menus/TasksMainMenu.js @@ -7,7 +7,10 @@ import { useSelector } from 'react-redux'; import { injectIntl } from 'react-intl'; import AssignmentIcon from '@material-ui/icons/Assignment'; import { formatMessage, MainMenuContribution, withModulesManager } from '@openimis/fe-core'; -import { TASKS_MANAGEMENT_MAIN_MENU_CONTRIBUTION_KEY } from '../constants'; +import { + RIGHT_TASKS_MANAGEMENT_SEARCH_ALL, + TASKS_MANAGEMENT_MAIN_MENU_CONTRIBUTION_KEY, +} from '../constants'; function TasksMainMenu(props) { const rights = useSelector((store) => store.core?.user?.i_user?.rights ?? []); @@ -17,6 +20,12 @@ function TasksMainMenu(props) { icon: , route: '/tasks', }, + { + text: formatMessage(props.intl, 'tasksManagement', 'entries.tasksManagementAllView'), + icon: , + route: '/AllTasks', + filter: (rights) => rights.includes(RIGHT_TASKS_MANAGEMENT_SEARCH_ALL), + }, ]; entries.push( ...props.modulesManager diff --git a/src/pages/TasksAllPage.js b/src/pages/TasksAllPage.js new file mode 100644 index 0000000..17c0f57 --- /dev/null +++ b/src/pages/TasksAllPage.js @@ -0,0 +1,38 @@ +/* eslint-disable react/destructuring-assignment */ +import React from 'react'; +import { useSelector } from 'react-redux'; +import { makeStyles } from '@material-ui/core/styles'; +import TaskAllSearcher from '../components/TaskAllSearcher'; + +import { + RIGHT_TASKS_MANAGEMENT_SEARCH_ALL, +} from '../constants'; + +const useStyles = makeStyles((theme) => ({ + page: theme.page, + paper: theme.paper.paper, + title: { + ...theme.paper.title, + cursor: 'pointer', + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + }, +})); + +function TasksAllPage() { + const rights = useSelector((store) => store.core?.user?.i_user?.rights ?? []); + const classes = useStyles(); + return ( +
+ {rights.includes(RIGHT_TASKS_MANAGEMENT_SEARCH_ALL) && ( + + )} +
+ ); +} + +export default TasksAllPage; diff --git a/src/pickers/TaskSourcesPicker.js b/src/pickers/TaskSourcesPicker.js new file mode 100644 index 0000000..fcf99f6 --- /dev/null +++ b/src/pickers/TaskSourcesPicker.js @@ -0,0 +1,25 @@ +import React from 'react'; +import { ConstantBasedPicker } from '@openimis/fe-core'; +import { TASK_AVAILABLE_SOURCES } from '../constants'; + +function TaskSourcesPicker(props) { + const { + required, withNull, readOnly, onChange, value, nullLabel, withLabel, + } = props; + return ( + + ); +} + +export default TaskSourcesPicker; diff --git a/src/pickers/TaskTypesPicker.js b/src/pickers/TaskTypesPicker.js new file mode 100644 index 0000000..6e1288d --- /dev/null +++ b/src/pickers/TaskTypesPicker.js @@ -0,0 +1,25 @@ +import React from 'react'; +import { ConstantBasedPicker } from '@openimis/fe-core'; +import { TASK_AVAILABLE_TYPES } from '../constants'; + +function TaskTypesPicker(props) { + const { + required, withNull, readOnly, onChange, value, nullLabel, withLabel, + } = props; + return ( + + ); +} + +export default TaskTypesPicker; diff --git a/src/translations/en.json b/src/translations/en.json index eff30d3..25c0666 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -52,5 +52,27 @@ "tasksManagement.task.resolve.confirm.fail.message": "Are you sure you want to fail this task?", "tasksManagement.menu.taskExecutionerGroups": "Task Executioner Groups", "tasksManagement.TaskSourcePicker.label": "Task Sources", - "tasksManagement.TaskSourcePicker.placeholder": "Search for task sources..." + "tasksManagement.TaskSourcePicker.placeholder": "Search for task sources...", + "tasksManagement.entries.tasksManagementAllView": "All Tasks", + "tasksManagement.task.source.mutationLabel": "Resolving task", + "tasksManagement.task.source.IndividualService": "Individual Service", + "tasksManagement.task.source.GroupIndividualService": "Group Individual Service", + "tasksManagement.task.source.CreateGroupAndMoveIndividualService": "Create Group And Move Individual Service", + "tasksManagement.task.source.BenefitPlanService": "Benefit Plan Service", + "tasksManagement.task.source.BeneficiaryService": "Beneficiary Service", + "tasksManagement.task.source.calcrule_social_protection": "Calculation Rule: Social Protection", + "tasksManagement.task.source.import_valid_items": "Import Valid Items", + "tasksManagement.task.source.payroll": "Payroll", + "tasksManagement.task.source.payroll_reconciliation": "Payroll Reconciliation", + "tasksManagement.task.source.payroll_reject": "Reject Payroll", + "tasksManagement.task.source.payroll_delete": "Delete Payroll", + "tasksManagement.task.source.CreateDeduplicationReviewTasksService": "Create Deduplication Review Tasks Service", + "tasksManagement.task.type.create": "Create", + "tasksManagement.task.type.update": "Update", + "tasksManagement.task.type.calculate": "Calculate", + "tasksManagement.task.type.import_valid_items": "Import Valid Items", + "tasksManagement.task.type.accept_payroll": "Reject Payroll", + "tasksManagement.task.type.payroll_reconciliation": "Payroll Reconciliation", + "tasksManagement.task.type.payroll_reject": "Reject Payroll", + "tasksManagement.task.type.payroll_delete": "Delete Payroll" }