Skip to content

Commit

Permalink
CM-968: added changelog for tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
sniedzielski committed Aug 2, 2024
1 parent 008f64a commit 6e69717
Show file tree
Hide file tree
Showing 7 changed files with 497 additions and 16 deletions.
23 changes: 23 additions & 0 deletions src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,24 @@ const TASK_PROJECTION = () => [
'jsonExt',
];

const TASK_HISTORY_FULL_PROJECTION = () => [
'id',
'entityId',
'source',
'status',
'executorActionEvent',
'businessEvent',
'businessStatus',
'dateCreated',
'isDeleted',
'taskGroup{id, code, completionPolicy, taskexecutorSet {edges{node{id, user{id}}}}}',
'data',
'businessData',
'jsonExt',
'version',
'dateUpdated',
];

export const formatTaskGroupGQL = (taskGroup) => {
const executors = taskGroup?.taskexecutorSet?.map((executor) => decodeId(executor.id));
const taskSources = taskGroup?.taskSources?.map((taskSource) => taskSource.name);
Expand Down Expand Up @@ -104,6 +122,11 @@ export function fetchTasks(modulesManager, params) {
return graphql(payload, ACTION_TYPE.SEARCH_TASKS);
}

export function fetchTaskHistory(modulesManager, params) {
const payload = formatPageQueryWithCount('taskHistory', params, TASK_HISTORY_FULL_PROJECTION());
return graphql(payload, ACTION_TYPE.SEARCH_TASK_HISTORY);
}

export function fetchTask(modulesManager, params) {
const payload = formatPageQueryWithCount('task', params, TASK_FULL_PROJECTION());
return graphql(payload, ACTION_TYPE.GET_TASK);
Expand Down
36 changes: 21 additions & 15 deletions src/components/TaskHeadPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import TaskStatusPicker from '../pickers/TaskStatusPicker';
import TaskGroupPicker from '../pickers/TaskGroupPicker';
import { TASK_STATUS, TASK_UPDATE } from '../constants';
import trimBusinessEvent from '../utils/trimBusinessEvent';
import TaskHistoryDialog from './dialogs/TaskHistoryDialog';

const styles = (theme) => ({
tableTitle: theme.table.title,
Expand All @@ -23,21 +24,26 @@ const styles = (theme) => ({
},
});

const renderHeadPanelTitle = (classes) => (
const renderHeadPanelTitle = (classes, rights, task) => (
<Grid container className={classes.tableTitle}>
<Grid item>
<Grid
container
align="center"
justify="center"
direction="column"
className={classes.fullHeight}
>
<Grid item>
<Typography>
<FormattedMessage module="tasksManagement" id="task.detailsPage.triage.headPanelTitle" />
</Typography>
</Grid>
<Grid
container
align="center"
justify="space-between"
direction="row"
className={classes.fullHeight}
>
<Grid item>
<Typography>
<FormattedMessage module="tasksManagement" id="task.detailsPage.triage.headPanelTitle" />
</Typography>
</Grid>
<Grid item>
<TaskHistoryDialog
classes={classes}
rights={rights}
taskId={task.id}
/>
</Grid>
</Grid>
</Grid>
Expand All @@ -51,7 +57,7 @@ class TaskHeadPanel extends FormPanel {
const task = { ...edited };
return (
<>
{renderHeadPanelTitle(classes)}
{renderHeadPanelTitle(classes, rights, task)}
<Divider />
<Grid container className={classes.item}>
<Grid item xs={3} className={classes.item}>
Expand Down
146 changes: 146 additions & 0 deletions src/components/TaskHistoryFilter.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 TaskHistoryFilter({
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)(TaskHistoryFilter)));
139 changes: 139 additions & 0 deletions src/components/TaskHistorySearcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
Searcher,
useHistory,
historyPush,
useModulesManager,
useTranslations,
} from '@openimis/fe-core';
import {
RIGHT_TASKS_MANAGEMENT_SEARCH, DEFAULT_PAGE_SIZE, ROWS_PER_PAGE_OPTIONS, TASK_STATUS, TASK_ROUTE,
} from '../constants';
import { fetchTaskHistory } from '../actions';
import trimBusinessEvent from '../utils/trimBusinessEvent';
import TaskHistoryFilter from './TaskHistoryFilter';

function TaskHistorySearcher({
rights, showFilters = true, taskId,
}) {
const history = useHistory();
const modulesManager = useModulesManager();
const dispatch = useDispatch();
const {
formatMessageWithValues,
formatDateTimeFromISO,
} = useTranslations('tasksManagement', modulesManager);

const fetchingTaskHistory = useSelector((state) => state?.tasksManagement?.fetchingTaskHistory);
const fetchedTaskHistory = useSelector((state) => state?.tasksManagement?.fetchedTaskHistory);
const errorTaskHistory = useSelector((state) => state?.tasksManagement?.errorTaskHistory);
const taskHistory = useSelector((state) => state?.tasksManagement?.taskHistory);
const taskHistoryPageInfo = useSelector((state) => state?.tasksManagement?.taskHistoryPageInfo);
const taskHistoryTotalCount = useSelector((state) => state?.tasksManagement?.taskHistoryTotalCount);

const openTask = (task, newTab = false) => historyPush(
modulesManager,
history,
TASK_ROUTE,
[task?.id],
newTab,
);

const onDoubleClick = (task) => openTask(task);
const fetch = (params) => dispatch(fetchTaskHistory(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.version',
'task.dateUpdated',
'task.status',
];
if (rights.includes(RIGHT_TASKS_MANAGEMENT_SEARCH)) {
headers.push('emptyLabel');
}
return headers;
};

const sorts = () => [
['source', true],
['type', false],
['businessStatus', true],
['taskGroup', true],
['version', true],
['dateUpdated', true],
['status', true],
];

const itemFormatters = () => [
(taskHistory) => taskHistory.source,
(taskHistory) => trimBusinessEvent(taskHistory.businessEvent),
(taskHistory) => taskHistory.businessStatus,
(taskHistory) => taskHistory?.taskGroup?.code,
(taskHistory) => taskHistory.version,
(taskHistory) => formatDateTimeFromISO(taskHistory?.dateUpdated),
(taskHistory) => taskHistory.status,
];

const defaultFilters = () => {
const filters = {
isDeleted: {
value: false,
filter: 'isDeleted: false',
},
};
if (taskId !== null && taskId !== undefined) {
filters.taskId = {
value: taskId,
filter: `id: "${taskId}"`,
};
}
return filters;
};

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

return (
<Searcher
module="tasksManagement"
FilterPane={showFilters && taskFilter}
fetch={fetch}
items={taskHistory}
itemsPageInfo={taskHistoryPageInfo}
fetchingItems={fetchingTaskHistory}
fetchedItems={fetchedTaskHistory}
errorItems={errorTaskHistory}
tableTitle={formatMessageWithValues('taskHistory.searcherResultsTitle', {
taskHistoryTotalCount,
})}
headers={headers}
itemFormatters={itemFormatters}
sorts={sorts}
rowsPerPageOptions={ROWS_PER_PAGE_OPTIONS}
defaultPageSize={DEFAULT_PAGE_SIZE}
defaultOrderBy="-version"
rowIdentifier={rowIdentifier}
onDoubleClick={onDoubleClick}
defaultFilters={defaultFilters()}
rowDisabled={isRowDisabled}
rights={rights}
/>
);
}

export default TaskHistorySearcher;
Loading

0 comments on commit 6e69717

Please sign in to comment.