Skip to content

Commit

Permalink
feat: [DHIS2-15483] assign an user when scheduling an enrollment event (
Browse files Browse the repository at this point in the history
  • Loading branch information
simonadomnisoru authored Nov 1, 2023
1 parent 9230e3a commit 556884f
Show file tree
Hide file tree
Showing 19 changed files with 152 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,8 @@ Feature: The user interacts with the widgets on the enrollment add event page
When you click switch tab to Schedule
Then you should see Schedule tab
And you should see suggested date: 08-01

Scenario: You can assign a user when scheduling an event
Given you land on the enrollment edit event page by having typed /#/enrollmentEventNew?enrollmentId=zRfAPUpjoG3&orgUnitId=DiszpKrYNg8&programId=M3xtLkYBlKI&stageId=uvMKOn1oWvd&teiId=S3JjTA4QMNe
When you click switch tab to Schedule
Then you can assign a user when scheduling the event
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
import { Then } from '@badeball/cypress-cucumber-preprocessor';
import '../sharedSteps';
import '../WidgetEnrollment';
import '../WidgetProfile';
import '../WidgetTab';

Then('you can assign a user when scheduling the event', () => {
cy.get('[data-test="assignee-section"]').within(() => {
cy.get('[data-test="capture-ui-input"]').click();
cy.get('[data-test="capture-ui-input"]').type('Tracker demo');
cy.contains('Tracker demo User').click();
});
cy.get('[data-test="assignee-section"]').within(() => {
cy.get('[data-test="dhis2-uicore-chip"]').contains('Tracker demo User').should('exist');
});
});
6 changes: 3 additions & 3 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,9 @@ msgstr "start typing to search"
msgid "suggestions could not be retrieved"
msgstr "suggestions could not be retrieved"

msgid "No results found"
msgstr "No results found"

msgid "No items to display"
msgstr "No items to display"

Expand Down Expand Up @@ -962,9 +965,6 @@ msgstr "Could not retrieve metadata. Please try again later."
msgid "Possible duplicates found"
msgstr "Possible duplicates found"

msgid "No results found"
msgstr "No results found"

msgid "An error occurred loading possible duplicates"
msgstr "An error occurred loading possible duplicates"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { UserField } from '../../../../../FormFields/UserField/UserField.compone
const getStyles = () => ({
container: {
display: 'flex',
alignItems: 'center',
alignItems: 'baseline',
padding: '8px 8px 8px 12px',
},
containerVertical: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// @flow
import * as React from 'react';
import { v4 as uuid } from 'uuid';
import { withStyles } from '@material-ui/core/styles';
import i18n from '@dhis2/d2-i18n';
import { colors } from '@dhis2/ui';
import { makeCancelablePromise } from 'capture-core-utils';
import { Input } from './Input.component';
import { SearchSuggestions } from './SearchSuggestions.component';
Expand All @@ -10,6 +12,13 @@ import type { User } from './types';
import { withApiUtils } from '../../../HOC';
import type { QuerySingleResource } from '../../../utils/api/api.types';

const getStyles = (theme: Theme) => ({
noMatchFound: {
color: colors.red600,
fontSize: theme.typography.pxToRem(14),
},
});

type Props = {
onSet: (user: User) => void,
inputWrapperClasses: Object,
Expand All @@ -18,6 +27,7 @@ type Props = {
inputPlaceholderText?: ?string,
useUpwardList?: ?boolean,
querySingleResource: QuerySingleResource,
...CssClasses,
};

type State = {
Expand All @@ -26,6 +36,7 @@ type State = {
suggestionsError?: ?string,
highlightedSuggestion?: ?User,
inputKey: number,
noMatch: boolean,
};

const exitBehaviours = {
Expand All @@ -46,6 +57,7 @@ class UserSearchPlain extends React.Component<Props, State> {
suggestions: [],
searchValue: '',
inputKey: 0,
noMatch: false,
};

this.domNames = {
Expand Down Expand Up @@ -82,6 +94,7 @@ class UserSearchPlain extends React.Component<Props, State> {
suggestions,
highlightedSuggestion: undefined,
searchValue,
noMatch: suggestions.length === 0,
});
}

Expand All @@ -90,6 +103,7 @@ class UserSearchPlain extends React.Component<Props, State> {
suggestions: [],
highlightedSuggestion: undefined,
searchValue: '',
noMatch: false,
});
}

Expand Down Expand Up @@ -133,11 +147,14 @@ class UserSearchPlain extends React.Component<Props, State> {
id: au.id,
name: au.displayName,
username: au.username,
firstName: au.firstName,
surname: au.surname,
}));
});

handleInputChange = (value: string) => {
this.cancelablePromise && this.cancelablePromise.cancel();
this.setState({ noMatch: false });

if (value.length > 1) {
const searchPromise = this.search(value);
Expand Down Expand Up @@ -294,18 +311,28 @@ class UserSearchPlain extends React.Component<Props, State> {
);
}

renderNoMatchFound() {
const { noMatch } = this.state;
const { classes } = this.props;

return noMatch ? (
<span className={classes.noMatchFound} >{i18n.t('No results found')}</span>
) : null;
}

render() {
return (
<div>
<SearchContext.Provider
value={this.domNames}
>
{this.renderInput()}
{this.renderNoMatchFound()}
{this.renderSuggestions()}
</SearchContext.Provider>
</div>
);
}
}

export const UserSearch = withApiUtils(UserSearchPlain);
export const UserSearch = withStyles(getStyles)(withApiUtils(UserSearchPlain));
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// @flow
export { UserField } from './UserField.component';
export type { User as UserFormField } from './types';
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ export type User = {
id: string,
username: string,
name: string,
firstName: string,
surname: string,
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { UserField } from '../../../FormFields/UserField/UserField.component';
const getStyles = () => ({
container: {
display: 'flex',
alignItems: 'center',
alignItems: 'baseline',
padding: '8px 8px 8px 12px',
},
containerVertical: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// @flow
import React from 'react';
import i18n from '@dhis2/d2-i18n';
import { withStyles } from '@material-ui/core/styles';
import { UserField } from '../../FormFields/UserField/UserField.component';
import type { Props } from './Assignee.types';

const getStyles = () => ({
container: {
display: 'flex',
alignItems: 'baseline',
padding: '8px 8px 8px 12px',
},
label: {
fontSize: 14,
flexBasis: 200,
color: '#212934',
},
field: {
flexBasis: 150,
flexGrow: 1,
},
});

const AssigneePlain = (props: Props) => {
const { classes, assignee, ...passOnProps } = props;
return (
<div className={classes.container}>
<div className={classes.label}>{i18n.t('Assignee')}</div>
<div className={classes.field}>
{/* $FlowFixMe[cannot-spread-inexact] automated comment */}
<UserField inputPlaceholderText={i18n.t('Search for user')} value={assignee} {...passOnProps} />
</div>
</div>
);
};

export const Assignee = withStyles(getStyles)(AssigneePlain);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// @flow
import type { UserFormField } from '../../FormFields/UserField';

export type Props = {
...CssClasses,
assignee?: UserFormField,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// @flow
export { Assignee } from './Assignee.component';
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const requestScheduleEvent = ({
onSaveExternal,
onSaveSuccessActionType,
onSaveErrorActionType,
assignedUser,
}: {
scheduleDate: string,
comments: Array<{value: string}>,
Expand All @@ -34,6 +35,7 @@ export const requestScheduleEvent = ({
onSaveExternal: (eventServerValues: Object, uid: string) => void,
onSaveSuccessActionType?: string,
onSaveErrorActionType?: string,
assignedUser?: {uid: string},
}) =>
actionCreator(scheduleEventWidgetActionTypes.EVENT_SCHEDULE_REQUEST)({
scheduleDate,
Expand All @@ -48,6 +50,7 @@ export const requestScheduleEvent = ({
onSaveExternal,
onSaveSuccessActionType,
onSaveErrorActionType,
assignedUser,
});

export const scheduleEvent = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ScheduleText } from './ScheduleText';
import { CommentSection } from '../WidgetComment';
import type { Props } from './widgetEventSchedule.types';
import { CategoryOptions } from './CategoryOptions/CategoryOptions.component';

import { Assignee } from './Assignee';

const styles = () => ({
wrapper: {
Expand Down Expand Up @@ -66,9 +66,12 @@ const WidgetEventSchedulePlain = ({
suggestedScheduleDate,
comments,
programCategory,
enableUserAssignment,
selectedCategories,
onClickCategoryOption,
onResetCategoryOption,
onSetAssignee,
assignee,
categoryOptionsError,
...passOnProps
}: Props) => (
Expand Down Expand Up @@ -119,6 +122,11 @@ const WidgetEventSchedulePlain = ({
handleAddComment={onAddComment}
/>
</DataSection>
{enableUserAssignment && (
<DataSection dataTest="assignee-section" sectionName={i18n.t('Assignee')}>
<Assignee onSet={onSetAssignee} assignee={assignee} />
</DataSection>
)}
<ScheduleButtons
hasChanges={scheduleDate !== suggestedScheduleDate}
onCancel={onCancel}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import { requestScheduleEvent } from './WidgetEventSchedule.actions';
import { NoAccess } from './AccessVerification';
import { useCategoryCombinations } from '../DataEntryDhis2Helpers/AOC/useCategoryCombinations';
import { convertAssigneeToServer } from '../../converters';

export const WidgetEventSchedule = ({
enrollmentId,
Expand All @@ -43,6 +44,7 @@ export const WidgetEventSchedule = ({
const { currentUser, noteId } = useCommentDetails();
const [scheduleDate, setScheduleDate] = useState('');
const [comments, setComments] = useState([]);
const [assignee, setAssignee] = useState();
const { events } = useEventsInOrgUnit(orgUnitId, scheduleDate);
const { eventId } = useLocationQuery();
const eventCountInOrgUnit = events
Expand Down Expand Up @@ -80,6 +82,7 @@ export const WidgetEventSchedule = ({
onSaveExternal: onSave,
onSaveSuccessActionType,
onSaveErrorActionType,
...(assignee && { assignedUser: convertAssigneeToServer(assignee) }),
}));
}, [
dispatch,
Expand All @@ -96,6 +99,7 @@ export const WidgetEventSchedule = ({
onSaveSuccessActionType,
onSaveErrorActionType,
programCategory,
assignee,
]);

React.useEffect(() => {
Expand All @@ -119,6 +123,7 @@ export const WidgetEventSchedule = ({
setComments([...comments, newComment]);
};

const onSetAssignee = useCallback(user => setAssignee(user), []);
const onClickCategoryOption = useCallback((optionId: string, categoryId: string) => {
setSelectedCategories(prevCategoryOptions => ({
...prevCategoryOptions,
Expand Down Expand Up @@ -159,11 +164,13 @@ export const WidgetEventSchedule = ({

return (
<WidgetEventScheduleComponent
assignee={assignee}
stageId={stageId}
stageName={stage.name}
programId={programId}
programCategory={programCategory}
programName={program.name}
enableUserAssignment={stage?.enableUserAssignment}
scheduleDate={scheduleDate}
displayDueDateLabel={programStageScheduleConfig.displayDueDateLabel}
suggestedScheduleDate={suggestedScheduleDate}
Expand All @@ -178,6 +185,7 @@ export const WidgetEventSchedule = ({
categoryOptionsError={categoryOptionsError}
onClickCategoryOption={onClickCategoryOption}
onResetCategoryOption={onResetCategoryOption}
onSetAssignee={onSetAssignee}
{...passOnProps}
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const scheduleEnrollmentEventEpic = (action$: InputObservable, store: Red
onSaveExternal,
onSaveSuccessActionType,
onSaveErrorActionType,
assignedUser,
} = action.payload;

const { events } = store.value;
Expand All @@ -46,6 +47,7 @@ export const scheduleEnrollmentEventEpic = (action$: InputObservable, store: Red
programStage: stageId,
status: 'SCHEDULE',
notes: comments ?? [],
assignedUser,
}] };

if (attributeCategoryOptions) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow
import type { ProgramCategory, CategoryOption } from './CategoryOptions/CategoryOptions.types';
import type { UserFormField } from '../FormFields/UserField';

export type ContainerProps = {|
programId: string,
Expand Down Expand Up @@ -38,7 +39,10 @@ export type Props = {|
selectedCategories?: ?{ [categoryId: string]: CategoryOption },
programCategory?: ProgramCategory,
categoryOptionsError?: ?{[categoryId: string]: { touched: boolean, valid: boolean} },
enableUserAssignment?: boolean,
onSchedule: () => void,
onSetAssignee: (user: UserFormField) => void,
assignee?: UserFormField,
onCancel: () => void,
setScheduleDate: (date: string) => void,
onAddComment: (comment: string) => void,
Expand Down
Loading

0 comments on commit 556884f

Please sign in to comment.