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

CM-880: standalone submenu items under 'Task' which will redirect to the page with one searcher for all tasks #48

Merged
merged 4 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
146 changes: 146 additions & 0 deletions src/components/TaskAllFilter.js
Original file line number Diff line number Diff line change
@@ -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 (
<Grid container className={classes.form}>
<Grid item xs={3} className={classes.item}>
<PublishedComponent
pubRef="tasksManagement.taskSourcesPicker"
module={MODULE_NAME}
withLabel
nullLabel={formatMessage(intl, MODULE_NAME, 'any')}
withNull
value={filterValue('source')}
onChange={(value) => onChangeFilters([
{
id: 'source',
value,
filter: value ? `source: "${value}"` : EMPTY_STRING,
},
])}
/>
</Grid>
<Grid item xs={3} className={classes.item}>
<PublishedComponent
pubRef="tasksManagement.taskTypesPicker"
module={MODULE_NAME}
withLabel
nullLabel={formatMessage(intl, MODULE_NAME, 'any')}
withNull
value={filterValue('businessEvent')}
onChange={onChangeStringFilter('businessEvent', CONTAINS_LOOKUP)}
/>
</Grid>
<Grid item xs={3} className={classes.item}>
<TextInput
module={MODULE_NAME}
label="task.entity"
value={filterTextFieldValue('entityString')}
onChange={onChangeStringFilter('entityString', CONTAINS_LOOKUP)}
/>
</Grid>
<Grid item xs={3} className={classes.item}>
<PublishedComponent
pubRef="tasksManagement.taskGroupPicker"
module={MODULE_NAME}
value={filterValue('taskGroupId')}
onChange={(value) => onChangeFilters([
{
id: 'taskGroupId',
value,
filter: value?.id ? `taskGroupId: "${decodeId(value.id)}"` : '',
},
])}
/>
</Grid>
<Grid item xs={3} className={classes.item}>
<PublishedComponent
pubRef="tasksManagement.taskStatusPicker"
module={MODULE_NAME}
withLabel
nullLabel={formatMessage(intl, MODULE_NAME, 'any')}
withNull
value={filterValue('status')}
onChange={(value) => onChangeFilters([
{
id: 'status',
value,
filter: value ? `status: ${value}` : EMPTY_STRING,
},
])}
/>
</Grid>
<Grid item xs={3} className={classes.item}>
<PublishedComponent
pubRef="core.DatePicker"
module={MODULE_NAME}
label={formatMessage(intl, MODULE_NAME, 'task.dateCreated.after')}
value={filterValue('dateCreated_Gte')}
onChange={(v) => onChangeFilters([
{
id: 'dateCreated_Gte',
value: v,
filter: `dateCreated_Gte: "${toISODateTime(v)}"`,
},
])}
/>
</Grid>
<Grid item xs={3} className={classes.item}>
<PublishedComponent
pubRef="core.DatePicker"
module={MODULE_NAME}
label={formatMessage(intl, MODULE_NAME, 'task.dateCreated.before')}
value={filterValue('dateCreated_Lte')}
onChange={(v) => onChangeFilters([
{
id: 'dateCreated_Lte',
value: v,
filter: `dateCreated_Lte: "${toISODateTime(v)}"`,
},
])}
/>
</Grid>
</Grid>
);
}

export default injectIntl(withTheme(withStyles(defaultFilterStyles)(TaskAllFilter)));
142 changes: 142 additions & 0 deletions src/components/TaskAllSearcher.js
Original file line number Diff line number Diff line change
@@ -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) => (
<Tooltip title={formatMessage('viewDetailsButton.tooltip')}>
<IconButton
onClick={() => openTask(task)}
>
<VisibilityIcon />
</IconButton>
</Tooltip>
),
];

const defaultFilters = () => {
const filters = {
isDeleted: {
value: false,
filter: 'isDeleted: false',
},
};
return filters;
};

const taskFilter = (props) => (
<TaskAllFilter
intl={props.intl}
classes={props.classes}
filters={props.filters}
onChangeFilters={props.onChangeFilters}
/>
);

return (
<Searcher
module="tasksManagement"
FilterPane={showFilters && taskFilter}
fetch={fetch}
items={tasks}
itemsPageInfo={tasksPageInfo}
fetchingItems={fetchingTasks}
fetchedItems={fetchedTasks}
errorItems={errorTasks}
tableTitle={formatMessageWithValues('task.searcherResultsTitle', {
tasksTotalCount,
})}
headers={headers}
itemFormatters={itemFormatters}
sorts={sorts}
rowsPerPageOptions={ROWS_PER_PAGE_OPTIONS}
defaultPageSize={DEFAULT_PAGE_SIZE}
defaultOrderBy="-dateCreated"
rowIdentifier={rowIdentifier}
onDoubleClick={onDoubleClick}
defaultFilters={defaultFilters()}
rowDisabled={isRowDisabled}
rights={rights}
/>
);
}

export default TaskAllSearcher;
27 changes: 27 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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',
];
7 changes: 7 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 },
Expand All @@ -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 },
Expand Down
11 changes: 10 additions & 1 deletion src/menus/TasksMainMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 ?? []);
Expand All @@ -17,6 +20,12 @@ function TasksMainMenu(props) {
icon: <AssignmentIcon />,
route: '/tasks',
},
{
text: formatMessage(props.intl, 'tasksManagement', 'entries.tasksManagementAllView'),
icon: <AssignmentIcon />,
route: '/AllTasks',
filter: (rights) => rights.includes(RIGHT_TASKS_MANAGEMENT_SEARCH_ALL),
},
];
entries.push(
...props.modulesManager
Expand Down
Loading
Loading