Skip to content

Commit

Permalink
chore: refactored
Browse files Browse the repository at this point in the history
  • Loading branch information
katrinan029 committed Nov 6, 2024
1 parent eba36ff commit 1c2dbf9
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 172 deletions.
4 changes: 2 additions & 2 deletions src/components/PeopleManagement/CreateGroupModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {
ActionRow, Button, FullscreenModal, StatefulButton, useToggle,
} from '@openedx/paragon';
import LmsApiService from '../../data/services/LmsApiService';
import InviteModalContent from '../learner-credit-management/invite-modal/InviteModalContent';
import SystemErrorAlertModal from '../learner-credit-management/cards/assignment-allocation-status-modals/SystemErrorAlertModal';
import CreateGroupModalContent from './CreateGroupModalContent';

const CreateGroupModal = ({
isModalOpen,
Expand Down Expand Up @@ -103,7 +103,7 @@ const CreateGroupModal = ({
</ActionRow>
)}
>
<InviteModalContent
<CreateGroupModalContent
onSetGroupName={setGroupName}
onEmailAddressesChange={handleEmailAddressesChange}
isGroupInvite
Expand Down
136 changes: 136 additions & 0 deletions src/components/PeopleManagement/CreateGroupModalContent.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import React, {
useCallback, useEffect, useMemo, useState,
} from 'react';
import PropTypes from 'prop-types';
import debounce from 'lodash.debounce';
import {
Col, Container, Form, Row,
} from '@openedx/paragon';
import { FormattedMessage } from '@edx/frontend-platform/i18n';

import InviteModalSummary from '../learner-credit-management/invite-modal/InviteModalSummary';
import InviteSummaryCount from '../learner-credit-management/invite-modal/InviteSummaryCount';
import FileUpload from '../learner-credit-management/invite-modal/FileUpload';
import { EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY, isInviteEmailAddressesInputValueValid } from '../learner-credit-management/cards/data';
import { MAX_LENGTH_GROUP_NAME } from './constants';

const CreateGroupModalContent = ({
onEmailAddressesChange,
onSetGroupName,
isGroupInvite,
}) => {
const [learnerEmails, setLearnerEmails] = useState([]);
const [emailAddressesInputValue, setEmailAddressesInputValue] = useState('');
const [memberInviteMetadata, setMemberInviteMetadata] = useState({
isValidInput: null,
lowerCasedEmails: [],
duplicateEmails: [],
});
const [groupNameLength, setGroupNameLength] = useState(0);
const [groupName, setGroupName] = useState('');

const handleGroupNameChange = useCallback((e) => {
if (!e.target.value) {
setGroupName('');
onSetGroupName('');
return;

Check warning on line 36 in src/components/PeopleManagement/CreateGroupModalContent.jsx

View check run for this annotation

Codecov / codecov/patch

src/components/PeopleManagement/CreateGroupModalContent.jsx#L34-L36

Added lines #L34 - L36 were not covered by tests
}
if (e.target.value.length > MAX_LENGTH_GROUP_NAME) {
return;

Check warning on line 39 in src/components/PeopleManagement/CreateGroupModalContent.jsx

View check run for this annotation

Codecov / codecov/patch

src/components/PeopleManagement/CreateGroupModalContent.jsx#L39

Added line #L39 was not covered by tests
}
setGroupName(e.target.value);
setGroupNameLength(e.target.value.length);
onSetGroupName(e.target.value);
}, [onSetGroupName]);

const handleEmailAddressesChanged = useCallback((value) => {
if (!value) {
setLearnerEmails([]);
onEmailAddressesChange([]);
return;
}
// handles csv upload value and formats emails into an array of strings
const emails = value.split('\n').map((email) => email.trim()).filter((email) => email.length > 0);
setLearnerEmails(emails);
}, [onEmailAddressesChange]);

const debouncedHandleEmailAddressesChanged = useMemo(
() => debounce(handleEmailAddressesChanged, EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY),
[handleEmailAddressesChanged],
);

useEffect(() => {
debouncedHandleEmailAddressesChanged(emailAddressesInputValue);
}, [emailAddressesInputValue, debouncedHandleEmailAddressesChanged]);

// Validate the learner emails emails from user input whenever it changes
useEffect(() => {
const inviteMetadata = isInviteEmailAddressesInputValueValid({
learnerEmails,
});
setMemberInviteMetadata(inviteMetadata);
if (inviteMetadata.canInvite) {
onEmailAddressesChange(learnerEmails, { canInvite: true });
} else {
onEmailAddressesChange([]);
}
}, [onEmailAddressesChange, learnerEmails]);

return (
<Container size="lg" className="py-3">
<h3>
<FormattedMessage
id="people.management.page.create.group.section.header"
defaultMessage="Create a custom group of members"
description="Header for the section to create a new group."
/>
</h3>
<Row>
<Col>
<h4 className="mt-4">Name your group</h4>
<Form.Control
value={groupName}
onChange={handleGroupNameChange}
label="name-your-group"
data-testid="group-name"
placeholder="Name"
/>
<Form.Control.Feedback className="mb-4">
{groupNameLength} / {MAX_LENGTH_GROUP_NAME}
</Form.Control.Feedback>
</Col>
<Col />
</Row>
<Row>
<Col>
<h4>Select group members</h4>
<p>
<FormattedMessage
id="people.management.page.create.group.csv.upload"
defaultMessage="Upload a CSV or select members from the table below."
description="Upload csv section and datatable with members to add to the new group."
/>
</p>
<FileUpload
memberInviteMetadata={memberInviteMetadata}
setEmailAddressesInputValue={setEmailAddressesInputValue}
/>
</Col>
<Col>
<h4>Details</h4>
<InviteModalSummary isGroupInvite={isGroupInvite} memberInviteMetadata={memberInviteMetadata} />
<InviteSummaryCount memberInviteMetadata={memberInviteMetadata} />
<hr className="my-4" />
</Col>
</Row>
</Container>
);
};

CreateGroupModalContent.propTypes = {
onEmailAddressesChange: PropTypes.func.isRequired,
onSetGroupName: PropTypes.func,
isGroupInvite: PropTypes.bool,
};

export default CreateGroupModalContent;
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const initialStoreState = {
enterpriseFeatures: {
topDownAssignmentRealTimeLcm: true,
enterpriseGroupsV1: true,
enterpriseGroupsV2: true,
},
},
};
Expand All @@ -57,7 +58,7 @@ const CreateGroupModalWrapper = ({
);
};

describe('<InviteMemberModal />', () => {
describe('<CreateGroupModal />', () => {
it('Modal renders as expected', async () => {
render(<CreateGroupModalWrapper />);
expect(screen.getByText('Create a custom group of members')).toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import PropTypes from 'prop-types';

const AssignmentModalFlexGroup = ({
const FlexGroupDropdown = ({
enterpriseFlexGroups,
onCheckedGroupsChanged,
checkedGroups,
Expand Down Expand Up @@ -50,7 +50,7 @@ const AssignmentModalFlexGroup = ({
);
};

AssignmentModalFlexGroup.propTypes = {
FlexGroupDropdown.propTypes = {
checkedGroups: PropTypes.shape({
id: PropTypes.string,
memberEmails: PropTypes.arrayOf(PropTypes.string),
Expand All @@ -71,4 +71,4 @@ AssignmentModalFlexGroup.propTypes = {
]),
};

export default AssignmentModalFlexGroup;
export default FlexGroupDropdown;
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';

import { connect } from 'react-redux';
import BaseCourseCard from '../cards/BaseCourseCard';
import { formatPrice, useBudgetId, useSubsidyAccessPolicy } from '../data';
import {
formatPrice,
useBudgetId,
useSubsidyAccessPolicy,
useGroupDropdownToggle,
} from '../data';
import AssignmentModalSummary from './AssignmentModalSummary';
import { EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY, isAssignEmailAddressesInputValueValid } from '../cards/data';
import AssignmentAllocationHelpCollapsibles from './AssignmentAllocationHelpCollapsibles';
import EVENT_NAMES from '../../../eventTracking';
import AssignmentModalFlexGroup from './AssignmentModalFlexGroup';
import useGroupDropdownToggle from '../data/hooks/useGroupDropdownToggle';
import FlexGroupDropdown from '../FlexGroupDropdown';
import { GROUP_DROPDOWN_TEXT } from '../../PeopleManagement/constants';

const AssignmentModalContent = ({
Expand Down Expand Up @@ -146,7 +150,7 @@ const AssignmentModalContent = ({
/>
</h4>
{shouldShowGroupsDropdown && (
<AssignmentModalFlexGroup
<FlexGroupDropdown
checkedGroups={checkedGroups}
dropdownRef={dropdownRef}
dropdownToggleLabel={dropdownToggleLabel}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const InviteMembersModalWrapper = ({
setRefresh,
refresh,
enterpriseId,
enterpriseFeatures,
}) => {
const { subsidyAccessPolicyId } = useBudgetId();
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
Expand All @@ -33,6 +34,7 @@ const InviteMembersModalWrapper = ({
const {
successfulInvitationToast: { displayToastForInvitation },
} = useContext(BudgetDetailPageContext);
const shouldShowGroupsDropdown = enterpriseFeatures.enterpriseGroupsV2 && enterpriseFlexGroups?.length > 0;

const handleCloseInviteModal = () => {
close();
Expand Down Expand Up @@ -130,6 +132,7 @@ const InviteMembersModalWrapper = ({
isGroupInvite={false}
onGroupSelectionsChanged={handleGroupSelectionsChanged}
enterpriseFlexGroups={enterpriseFlexGroups}
shouldShowGroupsDropdown={shouldShowGroupsDropdown}
/>
</FullscreenModal>
<SystemErrorAlertModal
Expand All @@ -149,10 +152,14 @@ InviteMembersModalWrapper.propTypes = {
setRefresh: PropTypes.func.isRequired,
refresh: PropTypes.bool.isRequired,
enterpriseId: PropTypes.string.isRequired,
enterpriseFeatures: PropTypes.shape({
enterpriseGroupsV2: PropTypes.bool.isRequired,
}),
};

const mapStateToProps = state => ({
enterpriseId: state.portalConfiguration.enterpriseId,
enterpriseFeatures: state.portalConfiguration.enterpriseFeatures,
});

export default connect(mapStateToProps)(InviteMembersModalWrapper);
Loading

0 comments on commit 1c2dbf9

Please sign in to comment.