Skip to content

Commit

Permalink
Merge branch 'master' into mashal-m/react-upgrade-to-v17
Browse files Browse the repository at this point in the history
  • Loading branch information
Mashal-m committed Jan 3, 2024
2 parents 09b7848 + 2d7e691 commit 66c3da9
Show file tree
Hide file tree
Showing 28 changed files with 980 additions and 129 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Chip } from '@edx/paragon';
import PropTypes from 'prop-types';
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import { connect } from 'react-redux';
import FailedBadEmail from './assignments-status-chips/FailedBadEmail';
import FailedCancellation from './assignments-status-chips/FailedCancellation';
import FailedRedemption from './assignments-status-chips/FailedRedemption';
Expand All @@ -8,14 +10,61 @@ import FailedSystem from './assignments-status-chips/FailedSystem';
import NotifyingLearner from './assignments-status-chips/NotifyingLearner';
import WaitingForLearner from './assignments-status-chips/WaitingForLearner';
import { capitalizeFirstLetter } from '../../utils';
import { useBudgetId, useSubsidyAccessPolicy } from './data';

const AssignmentStatusTableCell = ({ row }) => {
const AssignmentStatusTableCell = ({ enterpriseId, row }) => {
const { original } = row;
const {
learnerEmail,
learnerState,
errorReason,
} = original;
const { subsidyAccessPolicyId } = useBudgetId();
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
const {
subsidyUuid, assignmentConfiguration, isSubsidyActive, isAssignable, catalogUuid, aggregates,
} = subsidyAccessPolicy;

const sharedTrackEventMetadata = {
learnerState,
subsidyUuid,
assignmentConfiguration,
isSubsidyActive,
isAssignable,
catalogUuid,
aggregates,
};

const sendGenericTrackEvent = (eventName, eventMetadata = {}) => {
sendEnterpriseTrackEvent(
enterpriseId,
eventName,
{
...sharedTrackEventMetadata,
...eventMetadata,
},
);
};

const sendErrorStateTrackEvent = (eventName, eventMetadata = {}) => {
const errorReasonMetadata = {
erroredAction: {
errorReason: errorReason?.errorReason || null,
actionType: errorReason?.actionType || null,
},
};
const errorStateMetadata = {
...sharedTrackEventMetadata,
...errorReasonMetadata,
...eventMetadata,
};
sendEnterpriseTrackEvent(
enterpriseId,
eventName,
errorStateMetadata,
);
};

// Learner state is not available for this assignment, so don't display anything.
if (!learnerState) {
return null;
Expand All @@ -24,50 +73,50 @@ const AssignmentStatusTableCell = ({ row }) => {
// Display the appropriate status chip based on the learner state.
if (learnerState === 'notifying') {
return (
<NotifyingLearner learnerEmail={learnerEmail} />
<NotifyingLearner learnerEmail={learnerEmail} trackEvent={sendGenericTrackEvent} />
);
}

if (learnerState === 'waiting') {
return (
<WaitingForLearner learnerEmail={learnerEmail} />
<WaitingForLearner learnerEmail={learnerEmail} trackEvent={sendGenericTrackEvent} />
);
}

if (learnerState === 'failed') {
// If learnerState is failed but no top-level error reason is defined, return a failed system chip.
if (!errorReason) {
return <FailedSystem />;
return <FailedSystem trackEvent={sendErrorStateTrackEvent} />;
}
// Determine which failure chip to display based on the top level errorReason. In most cases, the actual errorReason
// code is ignored, in which case we key off the actionType.
if (errorReason.actionType === 'notified') {
if (errorReason.errorReason === 'email_error') {
return (
<FailedBadEmail learnerEmail={learnerEmail} />
<FailedBadEmail learnerEmail={learnerEmail} trackEvent={sendErrorStateTrackEvent} />
);
}
// non-email errors on failed notifications should NOT use the FailedBadEmail chip.
return <FailedSystem />;
return <FailedSystem trackEvent={sendErrorStateTrackEvent} />;
}
if (errorReason.actionType === 'cancelled') {
return <FailedCancellation />;
return <FailedCancellation trackEvent={sendErrorStateTrackEvent} />;
}
if (errorReason.actionType === 'reminded') {
return <FailedReminder />;
return <FailedReminder trackEvent={sendErrorStateTrackEvent} />;
}
if (errorReason.actionType === 'redeemed') {
return <FailedRedemption />;
return <FailedRedemption trackEvent={sendErrorStateTrackEvent} />;
}
// In all other unexpected cases, return a failed system chip.
return <FailedSystem />;
return <FailedSystem trackEvent={sendErrorStateTrackEvent} />;
}

// Note: The given `learnerState` not officially supported with a `ModalPopup`, but display it anyway.
return <Chip>{`${capitalizeFirstLetter(learnerState)}`}</Chip>;
};

AssignmentStatusTableCell.propTypes = {
enterpriseId: PropTypes.string.isRequired,
row: PropTypes.shape({
original: PropTypes.shape({
learnerEmail: PropTypes.string,
Expand All @@ -84,4 +133,8 @@ AssignmentStatusTableCell.propTypes = {
}).isRequired,
};

export default AssignmentStatusTableCell;
const mapStateToProps = state => ({
enterpriseId: state.portalConfiguration.enterpriseId,
});

export default connect(mapStateToProps)(AssignmentStatusTableCell);
101 changes: 94 additions & 7 deletions src/components/learner-credit-management/AssignmentTableCancel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Button } from '@edx/paragon';
import { DoNotDisturbOn } from '@edx/paragon/icons';
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import { connect } from 'react-redux';
import CancelAssignmentModal from './CancelAssignmentModal';
import useCancelContentAssignments from './data/hooks/useCancelContentAssignments';
import { transformSelectedRows, useBudgetId, useSubsidyAccessPolicy } from './data';
import EVENT_NAMES from '../../eventTracking';
import { getActiveTableColumnFilters } from '../../utils';

const calculateTotalToCancel = ({
Expand All @@ -17,9 +21,23 @@ const calculateTotalToCancel = ({
return assignmentUuids.length;
};

const AssignmentTableCancelAction = ({ selectedFlatRows, isEntireTableSelected, tableInstance }) => {
const assignmentUuids = selectedFlatRows.map(row => row.id);
const assignmentConfigurationUuid = selectedFlatRows[0].original.assignmentConfiguration;
const AssignmentTableCancelAction = ({
selectedFlatRows, isEntireTableSelected, learnerStateCounts, tableInstance, enterpriseId,
}) => {
const { subsidyAccessPolicyId } = useBudgetId();
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
const {
subsidyUuid, assignmentConfiguration, isSubsidyActive, isAssignable, catalogUuid, aggregates,
} = subsidyAccessPolicy;

const {
uniqueLearnerState,
uniqueAssignmentState,
uniqueContentKeys,
totalContentQuantity,
assignmentUuids,
totalSelectedRows,
} = transformSelectedRows(selectedFlatRows);

const activeFilters = getActiveTableColumnFilters(tableInstance.columns);

Expand All @@ -32,7 +50,66 @@ const AssignmentTableCancelAction = ({ selectedFlatRows, isEntireTableSelected,
close,
isOpen,
open,
} = useCancelContentAssignments(assignmentConfigurationUuid, assignmentUuids, shouldCancelAll);
} = useCancelContentAssignments(assignmentConfiguration.uuid, assignmentUuids, shouldCancelAll);

const {
BUDGET_DETAILS_ASSIGNED_DATATABLE_OPEN_BULK_CANCEL_MODAL,
BUDGET_DETAILS_ASSIGNED_DATATABLE_CLOSE_BULK_CANCEL_MODAL,
BUDGET_DETAILS_ASSIGNED_DATATABLE_BULK_CANCEL,
} = EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT;

const trackEvent = (eventName) => {
// constructs a learner state object for the select all state to match format of select all on page metadata
const learnerStateObject = {};
learnerStateCounts.forEach((learnerState) => {
learnerStateObject[learnerState.learnerState] = learnerState.count;
});

const selectedRowsMetadata = isEntireTableSelected
? { uniqueLearnerState: learnerStateObject, totalSelectedRows: tableInstance.itemCount }
: {
uniqueLearnerState, uniqueAssignmentState, uniqueContentKeys, totalContentQuantity, totalSelectedRows,
};

const trackEventMetadata = {
...selectedRowsMetadata,
isAssignable,
isSubsidyActive,
subsidyUuid,
catalogUuid,
isEntireTableSelected,
assignmentUuids,
aggregates,
assignmentConfiguration,
isOpen: !isOpen,
};

sendEnterpriseTrackEvent(
enterpriseId,
eventName,
trackEventMetadata,
);
};

const openModal = () => {
open();
trackEvent(
BUDGET_DETAILS_ASSIGNED_DATATABLE_OPEN_BULK_CANCEL_MODAL,
);
};

const closeModal = () => {
close();
trackEvent(
BUDGET_DETAILS_ASSIGNED_DATATABLE_CLOSE_BULK_CANCEL_MODAL,
);
};

const cancellationTrackEvent = () => {
trackEvent(
BUDGET_DETAILS_ASSIGNED_DATATABLE_BULK_CANCEL,
);
};

const tableItemCount = tableInstance.itemCount;
const totalToCancel = calculateTotalToCancel({
Expand All @@ -43,27 +120,37 @@ const AssignmentTableCancelAction = ({ selectedFlatRows, isEntireTableSelected,

return (
<>
<Button variant="danger" iconBefore={DoNotDisturbOn} onClick={open}>
<Button variant="danger" iconBefore={DoNotDisturbOn} onClick={openModal}>
{`Cancel (${totalToCancel})`}
</Button>
<CancelAssignmentModal
cancelContentAssignments={cancelContentAssignments}
close={close}
close={closeModal}
isOpen={isOpen}
cancelButtonState={cancelButtonState}
trackEvent={cancellationTrackEvent}
uuidCount={totalToCancel}
/>
</>
);
};

AssignmentTableCancelAction.propTypes = {
enterpriseId: PropTypes.string.isRequired,
selectedFlatRows: PropTypes.arrayOf(PropTypes.shape()).isRequired,
isEntireTableSelected: PropTypes.bool.isRequired,
learnerStateCounts: PropTypes.arrayOf(PropTypes.shape({
learnerState: PropTypes.string.isRequired,
count: PropTypes.number.isRequired,
})).isRequired,
tableInstance: PropTypes.shape({
itemCount: PropTypes.number.isRequired,
columns: PropTypes.arrayOf(PropTypes.shape()).isRequired,
}).isRequired,
};

export default AssignmentTableCancelAction;
const mapStateToProps = state => ({
enterpriseId: state.portalConfiguration.enterpriseId,
});

export default connect(mapStateToProps)(AssignmentTableCancelAction);
Loading

0 comments on commit 66c3da9

Please sign in to comment.