diff --git a/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx b/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx new file mode 100644 index 0000000000..1d771c27fa --- /dev/null +++ b/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx @@ -0,0 +1,64 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { + Icon, + IconButton, + OverlayTrigger, + Stack, + Tooltip, +} from '@edx/paragon'; +import { Mail, DoNotDisturbOn } from '@edx/paragon/icons'; + +const AssignmentRowActionTableCell = ({ row }) => { + const cancelButtonMarginLeft = row.original.state === 'allocated' ? 'ml-2.5' : 'ml-auto'; + return ( +
+ {row.original.state === 'allocated' && ( + <> + Remind learner} + > + console.log(`Reminding ${row.original.uuid}`)} + data-testid={`remind-assignment-${row.original.uuid}`} + /> + + + + )} + Cancel assignment} + > + console.log(`Canceling ${row.original.uuid}`)} + data-testid={`cancel-assignment-${row.original.uuid}`} + /> + +
+ ); +}; + +AssignmentRowActionTableCell.propTypes = { + row: PropTypes.shape({ + original: PropTypes.shape({ + uuid: PropTypes.string.isRequired, + state: PropTypes.string.isRequired, + }).isRequired, + }).isRequired, +}; + +export default AssignmentRowActionTableCell; diff --git a/src/components/learner-credit-management/AssignmentTableCancel.jsx b/src/components/learner-credit-management/AssignmentTableCancel.jsx new file mode 100644 index 0000000000..37c3e8ef6d --- /dev/null +++ b/src/components/learner-credit-management/AssignmentTableCancel.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Button } from '@edx/paragon'; +import { DoNotDisturbOn } from '@edx/paragon/icons'; + +const AssignmentTableCancelAction = ({ selectedFlatRows, ...rest }) => ( + // eslint-disable-next-line no-console + +); + +AssignmentTableCancelAction.propTypes = { + selectedFlatRows: PropTypes.arrayOf(PropTypes.shape()), +}; + +AssignmentTableCancelAction.defaultProps = { + selectedFlatRows: [], +}; + +export default AssignmentTableCancelAction; diff --git a/src/components/learner-credit-management/AssignmentTableRemind.jsx b/src/components/learner-credit-management/AssignmentTableRemind.jsx new file mode 100644 index 0000000000..bee8cc9814 --- /dev/null +++ b/src/components/learner-credit-management/AssignmentTableRemind.jsx @@ -0,0 +1,29 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Button } from '@edx/paragon'; +import { Mail } from '@edx/paragon/icons'; + +const AssignmentTableRemindAction = ({ selectedFlatRows, ...rest }) => { + const hideRemindAction = selectedFlatRows.some( + row => row.original.state !== 'allocated', + ); + if (hideRemindAction) { + return null; + } + return ( + // eslint-disable-next-line no-console + + ); +}; + +AssignmentTableRemindAction.propTypes = { + selectedFlatRows: PropTypes.arrayOf(PropTypes.shape()), +}; + +AssignmentTableRemindAction.defaultProps = { + selectedFlatRows: [], +}; + +export default AssignmentTableRemindAction; diff --git a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx index 301dfc99f8..cfc6603800 100644 --- a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx +++ b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx @@ -1,11 +1,13 @@ import React from 'react'; import PropTypes from 'prop-types'; import { DataTable } from '@edx/paragon'; - import TableTextFilter from './TableTextFilter'; import CustomDataTableEmptyState from './CustomDataTableEmptyState'; import AssignmentDetailsTableCell from './AssignmentDetailsTableCell'; import AssignmentStatusTableCell from './AssignmentStatusTableCell'; +import AssignmentRowActionTableCell from './AssignmentRowActionTableCell'; +import AssignmentTableRemindAction from './AssignmentTableRemind'; +import AssignmentTableCancelAction from './AssignmentTableCancel'; import { DEFAULT_PAGE, PAGE_SIZE, formatPrice } from './data'; import AssignmentRecentActionTableCell from './AssignmentRecentActionTableCell'; import AssignmentsTableRefreshAction from './AssignmentsTableRefreshAction'; @@ -19,6 +21,7 @@ const BudgetAssignmentsTable = ({ }) => ( , ]} @@ -68,6 +78,10 @@ const BudgetAssignmentsTable = ({ itemCount={tableData?.count || 0} pageCount={tableData?.numPages || 1} EmptyTableComponent={CustomDataTableEmptyState} + bulkActions={[ + , + , + ]} /> ); diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx index 3c5574ec22..462b32d46c 100644 --- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx +++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx @@ -723,4 +723,117 @@ describe('', () => { expect(screen.getByText('loading budget activity overview')).toBeInTheDocument(); }); + + it('displays remind row and bulk actions when allocated', async () => { + useParams.mockReturnValue({ + budgetId: mockSubsidyAccessPolicyUUID, + activeTabKey: 'activity', + }); + useOfferRedemptions.mockReturnValue({ + isLoading: false, + offerRedemptions: mockEmptyOfferRedemptions, + fetchOfferRedemptions: jest.fn(), + }); + useSubsidyAccessPolicy.mockReturnValue({ + isInitialLoading: false, + data: mockAssignableSubsidyAccessPolicy, + }); + useBudgetDetailActivityOverview.mockReturnValue({ + isLoading: false, + data: { + contentAssignments: { count: 1 }, + spentTransactions: { count: 0 }, + }, + }); + useBudgetContentAssignments.mockReturnValue({ + isLoading: false, + contentAssignments: { + count: 1, + results: [ + { + uuid: 'test-uuid', + contentKey: mockCourseKey, + contentQuantity: -19900, + learnerState: 'active', + recentAction: { actionType: 'assigned', timestamp: '2023-10-27' }, + actions: [], + state: 'allocated', + }, + ], + numPages: 1, + currentPage: 1, + }, + fetchContentAssignments: jest.fn(), + }); + renderWithRouter(); + const cancelRowAction = screen.getByTestId('cancel-assignment-test-uuid'); + const remindRowAction = screen.getByTestId('remind-assignment-test-uuid'); + expect(cancelRowAction).toBeInTheDocument(); + expect(remindRowAction).toBeInTheDocument(); + + const checkBox = screen.getByTestId('datatable-select-column-checkbox-cell'); + expect(checkBox).toBeInTheDocument(); + userEvent.click(checkBox); + await waitFor(() => { + expect(screen.getByText('Remind (1)')).toBeInTheDocument(); + }); + await waitFor(() => { + expect(screen.getByText('Cancel (1)')).toBeInTheDocument(); + }); + }); + + it('hides remind row and bulk actions when allocated', () => { + useOfferRedemptions.mockReturnValue({ + isLoading: false, + offerRedemptions: mockEmptyOfferRedemptions, + fetchOfferRedemptions: jest.fn(), + }); + useBudgetDetailActivityOverview.mockReturnValue({ + isLoading: false, + data: { + contentAssignments: { count: 1 }, + spentTransactions: { count: 0 }, + }, + }); + useParams.mockReturnValue({ + budgetId: mockSubsidyAccessPolicyUUID, + activeTabKey: 'activity', + }); + useSubsidyAccessPolicy.mockReturnValue({ + isInitialLoading: false, + data: mockAssignableSubsidyAccessPolicy, + }); + useBudgetDetailActivityOverview.mockReturnValue({ + isLoading: false, + data: { + contentAssignments: { count: 1 }, + spentTransactions: { count: 0 }, + }, + }); + useBudgetContentAssignments.mockReturnValue({ + isLoading: false, + contentAssignments: { + count: 1, + results: [ + { + uuid: 'test-uuid', + contentKey: mockCourseKey, + contentQuantity: -19900, + learnerState: 'accepted', + recentAction: { actionType: 'assigned', timestamp: '2023-10-27' }, + actions: [], + state: 'accepted', + }, + ], + numPages: 1, + currentPage: 1, + }, + fetchContentAssignments: jest.fn(), + }); + renderWithRouter(); + expect(screen.queryByTestId('remind-assignment-test-uuid')).not.toBeInTheDocument(); + const checkBox = screen.getByTestId('datatable-select-column-checkbox-cell'); + userEvent.click(checkBox); + expect(screen.queryByText('Remind (1)')).not.toBeInTheDocument(); + }); });