Skip to content

Commit

Permalink
feat: adding in remove member functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
kiram15 committed Dec 9, 2024
1 parent 7c43330 commit 8abd6de
Show file tree
Hide file tree
Showing 7 changed files with 410 additions and 144 deletions.
110 changes: 110 additions & 0 deletions src/components/PeopleManagement/GroupDetailPage/DownloadCsvButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React, { useState, useEffect } from 'react';
import { saveAs } from 'file-saver';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';

import {
Toast, StatefulButton, Icon, Spinner, useToggle,
} from '@openedx/paragon';
import { Download, Check } from '@openedx/paragon/icons';
import { logError } from '@edx/frontend-platform/logging';

const DownloadCsvButton = ({ data, testId, fetchData }) => {
const [buttonState, setButtonState] = useState('pageLoading');
const [isOpen, open, close] = useToggle(false);
const intl = useIntl();

useEffect(() => {
if (data && data.length) {
setButtonState('default');
}
}, [data]);

const getCsvFileName = () => {
const currentDate = new Date();
const year = currentDate.getUTCFullYear();
const month = currentDate.getUTCMonth() + 1;
const day = currentDate.getUTCDate();
return `${year}-${month}-${day}-group-detail-report.csv`;
};

const handleClick = async () => {
setButtonState('pending');
fetchData().then((response) => {
const blob = new Blob([response.data.results], {
type: 'text/csv',
});
saveAs(blob, getCsvFileName());
open();
setButtonState('complete');
}).catch((err) => {
logError(err);
});
};

const toastText = intl.formatMessage({
id: 'adminPortal.peopleManagement.groupDetail.downloadCsv.toast',
defaultMessage: 'Downloaded group members.',
description: 'Toast message for the download button on the group detail page.',
});
return (
<>
{ isOpen
&& (
<Toast onClose={close} show={isOpen}>
{toastText}
</Toast>
)}
<StatefulButton
state={buttonState}
className="download-button"
data-testid={testId}
labels={{
default: intl.formatMessage({
id: 'adminPortal.peopleManagement.groupDetail.downloadCsv.button',
defaultMessage: 'Download',
description: 'Label for the download button on the group detail page.',
}),
pending: intl.formatMessage({
id: 'adminPortal.peopleManagement.groupDetail.downloadCsv.button.pending',
defaultMessage: 'Downloading',
description: 'Label for the download button on the group detail page when the download is in progress.',
}),
complete: intl.formatMessage({
id: 'adminPortal.peopleManagement.groupDetail.downloadCsv.button.complete',
defaultMessage: 'Downloaded',
description: 'Label for the download button on the group detail page when the download is complete.',
}),
pageLoading: intl.formatMessage({
id: 'adminPortal.peopleManagement.groupDetail.downloadCsv.button.loading',
defaultMessage: 'Download',
description: 'Label for the download button on the group detail page when the page is loading.',
}),
}}
icons={{
default: <Icon src={Download} />,
pending: <Spinner animation="border" variant="light" size="sm" />,
complete: <Icon src={Check} />,
pageLoading: <Icon src={Download} variant="light" />,
}}
disabledStates={['pending', 'pageLoading']}
onClick={handleClick}
/>
</>
);
};

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

DownloadCsvButton.propTypes = {
// eslint-disable-next-line react/forbid-prop-types
data: PropTypes.arrayOf(
PropTypes.object,
),
fetchData: PropTypes.func.isRequired,
testId: PropTypes.string,
};

export default DownloadCsvButton;
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';

const GroupDetailPage = () => {
const intl = useIntl();
Expand All @@ -21,10 +21,13 @@ const GroupDetailPage = () => {
const [isEditModalOpen, openEditModal, closeEditModal] = useToggle(false);
const [isLoading, setIsLoading] = useState(true);
const [groupName, setGroupName] = useState(enterpriseGroup?.name);

const {
isLoading: isTableLoading,
enterpriseGroupLearnersTableData,
fetchEnterpriseGroupLearnersTableData,
refresh,
setRefresh,
} = useEnterpriseGroupLearnersTableData({ groupUuid });
const handleNameUpdate = (name) => {
setGroupName(name);
Expand Down Expand Up @@ -146,6 +149,8 @@ const GroupDetailPage = () => {
tableData={enterpriseGroupLearnersTableData}
fetchTableData={fetchEnterpriseGroupLearnersTableData}
groupUuid={groupUuid}
refresh={refresh}
setRefresh={setRefresh}
/>
</div>
);
Expand Down
200 changes: 200 additions & 0 deletions src/components/PeopleManagement/GroupDetailPage/GroupMembersTable.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
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 DownloadCsvButton from './DownloadCsvButton';
import LmsApiService from '../../../data/services/LmsApiService';
import RemoveMemberModal from './RemoveMemberModal';
import GeneralErrorModal from '../GeneralErrorModal';

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

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.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',
Header: DataTable.ControlledSelectHeader,
Cell: DataTable.ControlledSelect,
disableSortBy: true,
};

const GroupMembersTable = ({
isLoading,
tableData,
fetchTableData,
groupUuid,
refresh,
setRefresh,
}) => {
const fetchCsvData = async () => LmsApiService.fetchEnterpriseGroupLearners(
groupUuid,
// { ...currentFilters, search: searchQuery },
{ csv: true },
);
const intl = useIntl();
return (
<span className="budget-detail-assignments">
<DataTable
isSortable
manualSortBy
isSelectable
SelectionStatusComponent={DataTable.ControlledSelectionStatus}
manualSelectColumn={selectColumn}
isPaginated
manualPagination
isFilterable
manualFilters
isLoading={isLoading}
defaultColumnValues={{ Filter: TableTextFilter }}
FilterStatusComponent={FilterStatus}
numBreakoutFilters={2}
columns={[
{
Header: intl.formatMessage({
id: 'people.management.groups.detail.page.members.columns.memberDetails',
defaultMessage: 'Member details',
description:
'Column header for the Member details column in the People management Groups detail page',
}),
accessor: 'memberDetails',
Cell: MemberDetailsTableCell,
},
{
Header: intl.formatMessage({
id: 'people.management.groups.detail.page.members.columns.recentAction',
defaultMessage: 'Recent action',
description:
'Column header for the Recent action column in the People management Groups detail page',
}),
accessor: 'recentAction',
Cell: RecentActionTableCell,
disableFilters: true,
},
{
Header: EnrollmentsTableColumnHeader,
accessor: 'enrollmentCount',
Cell: ({ row }) => row.original.enrollments,
disableFilters: true,
},
]}
initialTableOptions={{
getRowId: (row) => row?.memberDetails.userEmail,
autoResetPage: true,
}}
initialState={{
pageSize: GROUP_MEMBERS_TABLE_PAGE_SIZE,
pageIndex: GROUP_MEMBERS_TABLE_DEFAULT_PAGE,
sortBy: [{ id: 'memberDetails', desc: true }],
filters: [],
}}
additionalColumns={[
{
id: 'action',
Header: '',
// eslint-disable-next-line react/no-unstable-nested-components
Cell: (props) => (
<KabobMenu
{...props}
groupUuid={groupUuid}
refresh={refresh}
setRefresh={setRefresh}
/>
),
},
]}
tableActions={[
<DownloadCsvButton
fetchData={fetchCsvData}
data={tableData.results}
testId="group-members-download"
/>,
]}
fetchData={fetchTableData}
data={tableData.results}
itemCount={tableData.itemCount}
pageCount={tableData.pageCount}
EmptyTableComponent={CustomDataTableEmptyState}
/>
</span>
);
};

GroupMembersTable.propTypes = {
isLoading: PropTypes.bool.isRequired,
tableData: PropTypes.shape({
results: PropTypes.arrayOf(PropTypes.shape({})),
itemCount: PropTypes.number.isRequired,
pageCount: PropTypes.number.isRequired,
}).isRequired,
fetchTableData: PropTypes.func.isRequired,
groupUuid: PropTypes.string.isRequired,
refresh: PropTypes.bool.isRequired,
setRefresh: PropTypes.func.isRequired,
};

export default GroupMembersTable;
Loading

0 comments on commit 8abd6de

Please sign in to comment.