Skip to content

Commit

Permalink
chore: rebase
Browse files Browse the repository at this point in the history
  • Loading branch information
katrinan029 committed Dec 10, 2024
2 parents 35f8f19 + 1a537e4 commit 5d9a849
Show file tree
Hide file tree
Showing 24 changed files with 849 additions and 331 deletions.
13 changes: 13 additions & 0 deletions src/components/PeopleManagement/AddMemberTableAction.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Button } from '@openedx/paragon';
import { Add } from '@openedx/paragon/icons';
import PropTypes from 'prop-types';

const AddMemberTableAction = ({ openModal }) => (
<Button iconBefore={Add} onClick={openModal} variant="outline-primary">Add members</Button>
);

AddMemberTableAction.propTypes = {
openModal: PropTypes.func.isRequired,
};

export default AddMemberTableAction;
70 changes: 70 additions & 0 deletions src/components/PeopleManagement/AddMembersBulkAction.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import PropTypes from 'prop-types';
import { StatefulButton } from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import { useGetAllEnterpriseLearnerEmails } from './data/hooks/useEnterpriseLearnersTableData';
import { getSelectedEmailsByRow } from './utils';

const AddMembersBulkAction = ({
isEntireTableSelected,
selectedFlatRows,
onHandleAddMembersBulkAction,
enterpriseId,
enterpriseGroupLearners,
}) => {
const intl = useIntl();
const { fetchLearnerEmails, addButtonState } = useGetAllEnterpriseLearnerEmails({
enterpriseId,
isEntireTableSelected,
onHandleAddMembersBulkAction,
enterpriseGroupLearners,
});
const handleOnClick = () => {
if (isEntireTableSelected) {
fetchLearnerEmails();
return;
}
const addedMemberEmails = enterpriseGroupLearners.map(learner => learner.memberDetails.userEmail);
const emails = getSelectedEmailsByRow(selectedFlatRows).filter(email => !addedMemberEmails.includes(email));
onHandleAddMembersBulkAction(emails);
};

return (
<StatefulButton
labels={{
default: intl.formatMessage({
id: 'people.management.add.new.group.modal.button',
defaultMessage: 'Add',
description: 'Button state text for adding members from datatable',
}),
pending: intl.formatMessage({
id: 'people.management.add.new.group.modal.pending',
defaultMessage: 'Adding...',
description: 'Button state text for adding members from datatable',
}),
complete: intl.formatMessage({
id: 'people.management.add.new.group.modal.complete',
defaultMessage: 'Add',
description: 'Button state text for adding members from datatable',
}),
error: intl.formatMessage({
id: 'people.management.add.new.group.modal.try.again',
defaultMessage: 'Try again',
description: 'Button state text for trying to add members again',
}),
}}
state={addButtonState}
onClick={handleOnClick}
disabledStates={['pending']}
/>
);
};

AddMembersBulkAction.propTypes = {
selectedFlatRows: PropTypes.arrayOf(PropTypes.shape()).isRequired,
enterpriseId: PropTypes.string.isRequired,
onHandleAddMembersBulkAction: PropTypes.func.isRequired,
isEntireTableSelected: PropTypes.bool,
enterpriseGroupLearners: PropTypes.arrayOf(PropTypes.string),
};

export default AddMembersBulkAction;
154 changes: 76 additions & 78 deletions src/components/PeopleManagement/AddMembersModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,56 +10,47 @@ import {
} from '@openedx/paragon';
import LmsApiService from '../../data/services/LmsApiService';
import SystemErrorAlertModal from '../learner-credit-management/cards/assignment-allocation-status-modals/SystemErrorAlertModal';
import AddGroupModalContent from './AddGroupModalContent';
import AddMembersModalContent from './AddMembersModalContent';
import { learnerCreditManagementQueryKeys } from '../learner-credit-management/data';
import { useAllEnterpriseGroupLearners } from './data/hooks';

const AddMembersModal = ({
isModalOpen,
closeModal,
enterpriseUUID,
groupName,
groupUuid,
}) => {
const intl = useIntl();
const [learnerEmails, setLearnerEmails] = useState([]);
const [createButtonState, setCreateButtonState] = useState('default');
const [canCreateGroup, setCanCreateGroup] = useState(false);
const [canInviteMembers, setCanInviteMembers] = useState(false);
const [addButtonState, setAddButtonState] = useState('default');
const [canAddMembers, setCanAddMembersGroup] = useState(false);
const [isSystemErrorModalOpen, openSystemErrorModal, closeSystemErrorModal] = useToggle(false);
const handleCloseCreateGroupModal = () => {
const handleCloseAddMembersModal = () => {
closeModal();
setCreateButtonState('default');
setAddButtonState('default');
};
const queryClient = useQueryClient();
const {
isLoading,
enterpriseGroupLearners,
} = useAllEnterpriseGroupLearners(groupUuid);

const handleCreateGroup = async () => {
setCreateButtonState('pending');
const options = {
enterpriseUUID,
groupName,
};
let groupCreationResponse;

try {
groupCreationResponse = await LmsApiService.createEnterpriseGroup(options);
} catch (err) {
logError(err);
setCreateButtonState('error');
openSystemErrorModal();
}

const handleAddMembers = async () => {
setAddButtonState('pending');
try {
const requestBody = snakeCaseObject({
learnerEmails,
});
await LmsApiService.inviteEnterpriseLearnersToGroup(groupCreationResponse.data.uuid, requestBody);
await LmsApiService.inviteEnterpriseLearnersToGroup(groupUuid, requestBody);
queryClient.invalidateQueries({
queryKey: learnerCreditManagementQueryKeys.group(enterpriseUUID),
queryKey: learnerCreditManagementQueryKeys.group(groupUuid),
});
setCreateButtonState('complete');
handleCloseCreateGroupModal();
setAddButtonState('complete');
handleCloseAddMembersModal();
} catch (err) {
logError(err);
setCreateButtonState('error');
setAddButtonState('error');
openSystemErrorModal();
}
};
Expand All @@ -69,71 +60,78 @@ const AddMembersModal = ({
{ canInvite = false } = {},
) => {
setLearnerEmails(value);
setCanInviteMembers(canInvite);
setCanAddMembersGroup(canInvite);
}, []);

useEffect(() => {
setCanCreateGroup(false);
if (canInviteMembers) {
setCanCreateGroup(true);
setCanAddMembersGroup(false);
if (canAddMembers) {
setCanAddMembersGroup(true);
}
}, [canInviteMembers]);
}, [canAddMembers]);

return (
<>
<FullscreenModal
className="stepper-modal bg-light-200"
isOpen={isModalOpen}
onClose={handleCloseCreateGroupModal}
title={intl.formatMessage({
id: 'peopleManagement.tab.create.group.modal.title',
defaultMessage: 'New group',
description: 'Title for creating a new group modal',
})}
footerNode={(
<ActionRow>
<ActionRow.Spacer />
<Button variant="tertiary" onClick={handleCloseCreateGroupModal}>Cancel</Button>
<StatefulButton
labels={{
default: 'Create',
pending: 'Creating...',
complete: 'Created',
error: 'Try again',
}}
variant="primary"
state={createButtonState}
disabled={!canCreateGroup}
onClick={handleCreateGroup}
<div>
{!isLoading ? (
<div>
<FullscreenModal
className="stepper-modal bg-light-200"
isOpen={isModalOpen}
onClose={handleCloseAddMembersModal}
title={intl.formatMessage({
id: 'peopleManagement.tab.add.members.modal.title',
defaultMessage: 'New group',
description: 'Title for adding members modal',
})}
footerNode={(
<ActionRow>
<ActionRow.Spacer />
<Button variant="tertiary" onClick={handleCloseAddMembersModal}>Cancel</Button>
<StatefulButton
labels={{
default: 'Add',
pending: 'Adding...',
complete: 'Added',
error: 'Try again',
}}
variant="primary"
state={addButtonState}
disabled={!canAddMembers}
onClick={handleAddMembers}
/>
</ActionRow>
)}
>
<AddMembersModalContent
groupName={groupName}
onEmailAddressesChange={handleEmailAddressesChange}
isGroupInvite
enterpriseUUID={enterpriseUUID}
enterpriseGroupLearners={enterpriseGroupLearners}
/>
</ActionRow>
)}
>
<AddGroupModalContent
groupName={groupName}
onEmailAddressesChange={handleEmailAddressesChange}
isGroupInvite
enterpriseUUID={enterpriseUUID}
/>
</FullscreenModal>
<SystemErrorAlertModal
isErrorModalOpen={isSystemErrorModalOpen}
closeErrorModal={closeSystemErrorModal}
closeAssignmentModal={handleCloseCreateGroupModal}
retry={handleCreateGroup}
/>
</>
</FullscreenModal>
<SystemErrorAlertModal
isErrorModalOpen={isSystemErrorModalOpen}
closeErrorModal={closeSystemErrorModal}
closeAssignmentModal={handleCloseAddMembersModal}
retry={handleAddMembers}
/>
</div>
) : null}
</div>
);
};

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

AddMembersModal.propTypes = {
enterpriseUUID: PropTypes.string.isRequired,
isModalOpen: PropTypes.bool.isRequired,
closeModal: PropTypes.func.isRequired,
groupUuid: PropTypes.string.isRequired,
groupName: PropTypes.string,
};

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

export default connect(mapStateToProps)(AddMembersModal);
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@ import React, {
import PropTypes from 'prop-types';
import debounce from 'lodash.debounce';
import {
Col, Container, Form, Row,
Col, Container, 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';
import EnterpriseCustomerUserDatatable from '../learner-credit-management/invite-modal/EnterpriseCustomerUserDatatable';
import EnterpriseCustomerUserDatatable from './EnterpriseCustomerUserDatatable';
import { useEnterpriseLearners } from '../learner-credit-management/data';

const AddGroupModalContent = ({
const AddMembersModalContent = ({
onEmailAddressesChange,
isGroupInvite,
enterpriseUUID,
groupName,
enterpriseGroupLearners,
}) => {
const [learnerEmails, setLearnerEmails] = useState([]);
const [emailAddressesInputValue, setEmailAddressesInputValue] = useState('');
Expand All @@ -32,7 +32,6 @@ const AddGroupModalContent = ({
});
const { allEnterpriseLearners } = useEnterpriseLearners({ enterpriseUUID });


const handleAddMembersBulkAction = useCallback((value) => {
if (!value) {
setLearnerEmails([]);
Expand Down Expand Up @@ -129,15 +128,18 @@ const AddGroupModalContent = ({
onHandleAddMembersBulkAction={handleAddMembersBulkAction}
onHandleRemoveMembersBulkAction={handleRemoveMembersBulkAction}
learnerEmails={learnerEmails}
enterpriseGroupLearners={enterpriseGroupLearners}
/>
</Container>
);
};

AddGroupModalContent.propTypes = {
AddMembersModalContent.propTypes = {
onEmailAddressesChange: PropTypes.func.isRequired,
isGroupInvite: PropTypes.bool,
enterpriseUUID: PropTypes.string.isRequired,
groupName: PropTypes.string,
enterpriseGroupLearners: PropTypes.arrayOf(PropTypes.shape({})),
};

export default AddGroupModalContent;
export default AddMembersModalContent;
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import InviteSummaryCount from '../learner-credit-management/invite-modal/Invite
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';
import EnterpriseCustomerUserDatatable from '../learner-credit-management/invite-modal/EnterpriseCustomerUserDatatable';
import EnterpriseCustomerUserDatatable from './EnterpriseCustomerUserDatatable';
import { useEnterpriseLearners } from '../learner-credit-management/data';

const CreateGroupModalContent = ({
Expand Down
40 changes: 40 additions & 0 deletions src/components/PeopleManagement/EnrollmentsTableColumnHeader.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import {
OverlayTrigger,
Tooltip,
Stack,
Icon,
} from '@openedx/paragon';
import { InfoOutline } from '@openedx/paragon/icons';
import { FormattedMessage } from '@edx/frontend-platform/i18n';

const EnrollmentsTableColumnHeader = () => (
<Stack gap={1} direction="horizontal">
<span data-testid="members-table-enrollments-column-header">
<FormattedMessage
id="people.management.groups.detail.page.learnersTable.enrollmentsColumn"
defaultMessage="Enrollments"
description="Enrollments column header in the Members table"
/>
</span>
<OverlayTrigger
key="enrollments-column-tooltip"
placement="top"
overlay={(
<Tooltip id="enrollments-column-tooltip">
<div>
<FormattedMessage
id="people.management.groups.detail.page.learnersTable.enrollmentsColumn.tooltip"
defaultMessage="Total number of enrollment originated from the budget"
description="Tooltip for the Enrollments column header in the Group Members table"
/>
</div>
</Tooltip>
)}
>
<Icon size="xs" src={InfoOutline} className="ml-1 d-inline-flex" />
</OverlayTrigger>
</Stack>
);

export default EnrollmentsTableColumnHeader;
Loading

0 comments on commit 5d9a849

Please sign in to comment.