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

Adding in remove capability for group members + csv download #1367

Merged
merged 15 commits into from
Jan 10, 2025
Merged
4 changes: 2 additions & 2 deletions src/components/PeopleManagement/CreateGroupModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import LmsApiService from '../../data/services/LmsApiService';
import SystemErrorAlertModal from '../learner-credit-management/cards/assignment-allocation-status-modals/SystemErrorAlertModal';
import CreateGroupModalContent from './CreateGroupModalContent';
import { learnerCreditManagementQueryKeys } from '../learner-credit-management/data';
import { peopleManagementQueryKeys } from './constants';

const CreateGroupModal = ({
isModalOpen,
Expand Down Expand Up @@ -53,7 +53,7 @@ const CreateGroupModal = ({
});
await LmsApiService.inviteEnterpriseLearnersToGroup(groupCreationResponse.data.uuid, requestBody);
queryClient.invalidateQueries({
queryKey: learnerCreditManagementQueryKeys.group(enterpriseUUID),
queryKey: peopleManagementQueryKeys.group(enterpriseUUID),
});
setCreateButtonState('complete');
handleCloseCreateGroupModal();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import { useEnterpriseLearnersTableData } from './data/hooks/useEnterpriseLearnersTableData';
import { GROUP_MEMBERS_TABLE_DEFAULT_PAGE, GROUP_MEMBERS_TABLE_PAGE_SIZE } from './constants';
import MemberDetailsCell from './MemberDetailsCell';
import AddMembersBulkAction from './AddMembersBulkAction';
import AddMembersBulkAction from './GroupDetailPage/AddMembersBulkAction';
import RemoveMembersBulkAction from './RemoveMembersBulkAction';
import MemberJoinedDateCell from './MemberJoinedDateCell';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ 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>
<Button
className="align-top"
iconBefore={Add}
onClick={openModal}
variant="outline-primary"
>Add members
</Button>
);

AddMemberTableAction.propTypes = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
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';
import { useGetAllEnterpriseLearnerEmails } from '../data/hooks/useEnterpriseLearnersTableData';
import { getSelectedEmailsByRow } from '../utils';

const AddMembersBulkAction = ({
isEntireTableSelected,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React from 'react';
import PropTypes from 'prop-types';
import { defineMessages, useIntl } from '@edx/frontend-platform/i18n';

import {
Icon, IconButtonWithTooltip, Toast, useToggle,
} from '@openedx/paragon';
import { Download } from '@openedx/paragon/icons';
import { logError } from '@edx/frontend-platform/logging';
import GeneralErrorModal from '../GeneralErrorModal';
import { downloadCsv, getTimeStampedFilename } from '../../../utils';

const csvHeaders = ['Name', 'Email', 'Recent action', 'Enrollments'];

const DownloadCsvIconButton = ({ fetchAllData, dataCount, testId }) => {
const [isToastOpen, openToast, closeToast] = useToggle(false);
const [isErrorModalOpen, openErrorModal, closeErrorModal] = useToggle(false);
const intl = useIntl();
const messages = defineMessages({
downloadToastText: {
id: 'adminPortal.peopleManagement.groupDetail.downloadCsv.toast',
defaultMessage: 'Downloaded group members',
description: 'Toast message for the download button on the group detail page.',
},
downloadHoverText: {
id: 'adminPortal.peopleManagement.groupDetail.downloadCsv.hoverTooltip',
defaultMessage: `Download (${dataCount})`,
description: 'Tooltip message for the download button on the group detail page.',
},
});

const dataEntryToRow = (entry) => {
const { memberDetails: { userEmail, userName }, recentAction, enrollments } = entry;
return [userName, userEmail, recentAction, enrollments];

Check warning on line 34 in src/components/PeopleManagement/GroupDetailPage/DownloadCsvIconButton.jsx

View check run for this annotation

Codecov / codecov/patch

src/components/PeopleManagement/GroupDetailPage/DownloadCsvIconButton.jsx#L33-L34

Added lines #L33 - L34 were not covered by tests
};

const handleClick = async () => {
fetchAllData().then((response) => {
const fileName = getTimeStampedFilename('group-report.csv');
downloadCsv(fileName, response.results, csvHeaders, dataEntryToRow);
openToast();
}).catch((err) => {
logError(err);
openErrorModal();
});
};

return (
<>
{ isToastOpen
&& (
<Toast onClose={closeToast} show={isToastOpen}>
{intl.formatMessage(messages.downloadToastText)}
</Toast>
)}
<GeneralErrorModal
isOpen={isErrorModalOpen}
close={closeErrorModal}
/>
<IconButtonWithTooltip
data-testid={testId}
tooltipContent={intl.formatMessage(messages.downloadHoverText)}
src={Download}
iconAs={Icon}
alt="Download group members"
variant="primary"
onClick={handleClick}
/>
</>
);
};

DownloadCsvIconButton.defaultProps = {
testId: 'download-csv-icon-button',
};

DownloadCsvIconButton.propTypes = {
fetchAllData: PropTypes.func.isRequired,
dataCount: PropTypes.number.isRequired,
testId: PropTypes.string,
};

export default DownloadCsvIconButton;
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ROUTE_NAMES } from '../../EnterpriseApp/data/constants';
import DeleteGroupModal from './DeleteGroupModal';
import EditGroupNameModal from './EditGroupNameModal';
import formatDates from '../utils';
import GroupMembersTable from '../GroupMembersTable';
import GroupMembersTable from './GroupMembersTable';
import AddMembersModal from '../AddMembersModal/AddMembersModal';

const GroupDetailPage = () => {
Expand All @@ -27,6 +27,9 @@ const GroupDetailPage = () => {
isLoading: isTableLoading,
enterpriseGroupLearnersTableData,
fetchEnterpriseGroupLearnersTableData,
fetchAllEnterpriseGroupLearnersData,
refresh,
setRefresh,
} = useEnterpriseGroupLearnersTableData({ groupUuid, isAddMembersModalOpen });
const handleNameUpdate = (name) => {
setGroupName(name);
Expand Down Expand Up @@ -108,7 +111,6 @@ const GroupDetailPage = () => {
<IconButtonWithTooltip
alt="icon to trash group"
key="trashGroupTooltip"
tooltipPlacement="top"
tooltipContent={tooltipContent}
src={Delete}
iconAs={Icon}
Expand Down Expand Up @@ -147,7 +149,11 @@ const GroupDetailPage = () => {
isLoading={isTableLoading}
tableData={enterpriseGroupLearnersTableData}
fetchTableData={fetchEnterpriseGroupLearnersTableData}
fetchAllData={fetchAllEnterpriseGroupLearnersData}
dataCount={enterpriseGroupLearnersTableData.itemCount}
groupUuid={groupUuid}
refresh={refresh}
setRefresh={setRefresh}
openAddMembersModal={openAddMembersModal}
/>
<AddMembersModal
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,77 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
DataTable, Dropdown, Icon, IconButton,
DataTable, Dropdown, Icon, IconButton, useToggle,
} from '@openedx/paragon';
import { MoreVert, RemoveCircle } from '@openedx/paragon/icons';
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
import TableTextFilter from '../learner-credit-management/TableTextFilter';
import CustomDataTableEmptyState from '../learner-credit-management/CustomDataTableEmptyState';
import MemberDetailsTableCell from '../learner-credit-management/members-tab/MemberDetailsTableCell';
import EnrollmentsTableColumnHeader from './EnrollmentsTableColumnHeader';
import { GROUP_MEMBERS_TABLE_DEFAULT_PAGE, GROUP_MEMBERS_TABLE_PAGE_SIZE } from './constants';
import RecentActionTableCell from './RecentActionTableCell';

import TableTextFilter from '../../learner-credit-management/TableTextFilter';
import CustomDataTableEmptyState from '../../learner-credit-management/CustomDataTableEmptyState';
import MemberDetailsTableCell from '../../learner-credit-management/members-tab/MemberDetailsTableCell';
import EnrollmentsTableColumnHeader from '../EnrollmentsTableColumnHeader';
import {
GROUP_MEMBERS_TABLE_DEFAULT_PAGE,
GROUP_MEMBERS_TABLE_PAGE_SIZE,
} from '../constants';
import RecentActionTableCell from '../RecentActionTableCell';
import DownloadCsvIconButton from './DownloadCsvIconButton';
import RemoveMemberModal from './RemoveMemberModal';
import GeneralErrorModal from '../GeneralErrorModal';
import AddMemberTableAction from './AddMemberTableAction';

const FilterStatus = (rest) => <DataTable.FilterStatus showFilteredFields={false} {...rest} />;

const KabobMenu = () => (
<Dropdown drop="top">
<Dropdown.Toggle
id="kabob-menu-dropdown"
data-testid="kabob-menu-dropdown"
as={IconButton}
src={MoreVert}
iconAs={Icon}
variant="primary"
/>
<Dropdown.Menu>
<Dropdown.Item>
<Icon src={RemoveCircle} className="mr-2 text-danger-500" />
<FormattedMessage
id="people.management.budgetDetail.membersTab.kabobMenu.removeMember"
defaultMessage="Remove member"
description="Remove member option in the kabob menu"
const KabobMenu = ({
row, groupUuid, refresh, setRefresh,
}) => {
const [isRemoveModalOpen, openRemoveModal, closeRemoveModal] = useToggle(false);
const [isErrorModalOpen, openErrorModal, closeErrorModal] = useToggle(false);
return (
<>
<RemoveMemberModal
groupUuid={groupUuid}
row={row}
isOpen={isRemoveModalOpen}
close={closeRemoveModal}
openError={openErrorModal}
refresh={refresh}
setRefresh={setRefresh}
/>
<GeneralErrorModal
isOpen={isErrorModalOpen}
close={closeErrorModal}
/>
<Dropdown drop="top">
<Dropdown.Toggle
id="kabob-menu-dropdown"
data-testid="kabob-menu-dropdown"
as={IconButton}
src={MoreVert}
iconAs={Icon}
variant="primary"
/>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);
<Dropdown.Menu>
<Dropdown.Item onClick={openRemoveModal}>
<Icon src={RemoveCircle} className="mr-2 text-danger-500" />
<FormattedMessage
id="people.management.budgetDetail.membersTab.kabobMenu.removeMember"
defaultMessage="Remove member"
description="Remove member option in the kabob menu"
/>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</>
);
};

KabobMenu.propTypes = {
row: PropTypes.shape({}).isRequired,
groupUuid: PropTypes.string.isRequired,
refresh: PropTypes.bool.isRequired,
setRefresh: PropTypes.func.isRequired,
};

const selectColumn = {
id: 'selection',
Expand All @@ -49,7 +84,11 @@ const GroupMembersTable = ({
isLoading,
tableData,
fetchTableData,
fetchAllData,
dataCount,
groupUuid,
refresh,
setRefresh,
openAddMembersModal,
}) => {
const intl = useIntl();
Expand All @@ -69,9 +108,6 @@ const GroupMembersTable = ({
defaultColumnValues={{ Filter: TableTextFilter }}
FilterStatusComponent={FilterStatus}
numBreakoutFilters={2}
tableActions={[
<AddMemberTableAction openModal={openAddMembersModal} />,
]}
columns={[
{
Header: intl.formatMessage({
Expand Down Expand Up @@ -106,9 +142,7 @@ const GroupMembersTable = ({
initialState={{
pageSize: GROUP_MEMBERS_TABLE_PAGE_SIZE,
pageIndex: GROUP_MEMBERS_TABLE_DEFAULT_PAGE,
sortBy: [
{ id: 'memberDetails', desc: true },
],
sortBy: [{ id: 'memberDetails', desc: true }],
filters: [],
}}
additionalColumns={[
Expand All @@ -117,10 +151,23 @@ const GroupMembersTable = ({
Header: '',
// eslint-disable-next-line react/no-unstable-nested-components
Cell: (props) => (
<KabobMenu {...props} groupUuid={groupUuid} />
<KabobMenu
{...props}
groupUuid={groupUuid}
refresh={refresh}
setRefresh={setRefresh}
/>
),
},
]}
tableActions={[
<AddMemberTableAction openModal={openAddMembersModal} />,
<DownloadCsvIconButton
fetchAllData={fetchAllData}
dataCount={dataCount}
testId="group-members-download"
/>,
]}
fetchData={fetchTableData}
data={tableData.results}
itemCount={tableData.itemCount}
Expand All @@ -134,13 +181,16 @@ const GroupMembersTable = ({
GroupMembersTable.propTypes = {
isLoading: PropTypes.bool.isRequired,
tableData: PropTypes.shape({
results: PropTypes.arrayOf(PropTypes.shape({
})),
results: PropTypes.arrayOf(PropTypes.shape({})),
itemCount: PropTypes.number.isRequired,
pageCount: PropTypes.number.isRequired,
}).isRequired,
fetchTableData: PropTypes.func.isRequired,
fetchAllData: PropTypes.func.isRequired,
dataCount: PropTypes.number.isRequired,
groupUuid: PropTypes.string.isRequired,
refresh: PropTypes.bool.isRequired,
setRefresh: PropTypes.func.isRequired,
openAddMembersModal: PropTypes.func.isRequired,
};

Expand Down
Loading
Loading