Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const ApprovedRequestActionsTableCell = ({ row }) => {

// Check if the cancel and remind button should be shown for this row
const shouldShowShowActionButtons = (
(original.lastActionStatus === 'waiting_for_learner' || original.requestStatus === 'approved')
(original.lastActionStatus === 'reminded' || original.requestStatus === 'approved')
);

// Don't render dropdown if no actions are available
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,14 @@ const FilterStatus = (rest) => (
);

const getRequestStatusDisplayName = (status) => {
if (status === 'waiting_for_learner') {
return 'Waiting for learner';
}
const statusDisplayMap = {
reminded: 'Waiting for learner',
approved: 'Approved',
pending: 'Pending',
refunded: 'Refunded',
};

if (status === 'refunded') {
return 'Refunded';
}

if (status === 'failed_cancellation') {
return 'Failed cancellation';
}

return status
return statusDisplayMap[status] || status
.split('_')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const CancelApprovedRequestModal = ({
<p>
<FormattedMessage
id="lcm.budget.detail.page.approved.requests.cancel.approval.modal.body2"
defaultMessage="The learner will be notified that their approved request has been canceled. The funds associated with this request will move from 'assigned' back to 'available'."
defaultMessage="The learner will be notified that their approved request has been canceled. The funds associated with this request will move from 'pending' back to 'available'."
description="Body text for the cancel approval modal which informs the user that the learner will be notified that the approval has been canceled."
/>
</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ const RequestStatusTableCell = ({ enterpriseId, row }) => {
// Currently we check both `lastActionErrorReason` and `lastActionStatus` which creates
// confusion since status information comes from two different sources. The API should
// be updated to return a single, unified status field to simplify this logic.
if (lastActionErrorReason === 'Failed: Cancellation') {

if (lastActionErrorReason === 'failed_cancellation') {
return (
<FailedCancellation
learnerEmail={learnerEmail}
Expand All @@ -49,7 +50,7 @@ const RequestStatusTableCell = ({ enterpriseId, row }) => {
);
}

if (lastActionStatus === 'waiting_for_learner' || requestStatus === 'approved') {
if (lastActionStatus === 'reminded' || requestStatus === 'approved') {
return (
<WaitingForLearner
learnerEmail={learnerEmail}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const mockApiResponse = {
created: '2023-10-27T10:00:00Z',
state: 'approved',
latestAction: {
status: 'waiting for learner',
status: 'reminded',
errorReason: null,
created: '2023-10-27T10:00:00Z',
},
Expand Down Expand Up @@ -71,11 +71,11 @@ const expectedTransformedData = [
amount: 100,
requestDate: 'Oct 27, 2023',
requestStatus: 'approved',
lastActionStatus: 'waiting_for_learner',
lastActionStatus: 'reminded',
lastActionDate: 'Oct 27, 2023',
lastActionErrorReason: null,
latestAction: {
status: 'waiting for learner',
status: 'reminded',
errorReason: null,
created: '2023-10-27T10:00:00Z',
},
Expand All @@ -90,10 +90,10 @@ const expectedTransformedData = [
requestStatus: 'approved',
lastActionStatus: 'refunded',
lastActionDate: 'Oct 27, 2023',
lastActionErrorReason: 'Payment failed',
lastActionErrorReason: 'failed_cancellation',
latestAction: {
status: 'refunded',
errorReason: 'Payment failed',
errorReason: 'failed_cancellation',
created: '2023-10-27T11:00:00Z',
},
},
Expand Down Expand Up @@ -247,8 +247,8 @@ describe('useBnrSubsidyRequests', () => {
{
page: 1,
page_size: 25,
search: '[email protected]',
state: 'approved,pending',
search: '[email protected]',
},
);
});
Expand Down Expand Up @@ -528,10 +528,24 @@ describe('applyFiltersToOptions', () => {
});
});

it('should not apply status filter when array is empty', () => {
it('should apply nested field filters correctly', () => {
const options = {};
applyFiltersToOptions([
{ id: 'lastActionStatus', value: ['reminded', 'approved'] },
{ id: 'lastActionErrorReason', value: ['failed_cancellation'] },
], options);
expect(options).toEqual({
action_status: 'reminded,approved',
action_error_reason: 'failed_cancellation',
});
});

it('should not apply filters when values are empty', () => {
const options = {};
applyFiltersToOptions([
{ id: 'requestStatus', value: [] },
{ id: 'requestDetails', value: '' },
{ id: 'lastActionStatus', value: [] },
], options);
expect(options).toEqual({});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ const API_FIELDS_BY_TABLE_COLUMN_ACCESSOR = {
amount: 'amount',
requestDate: 'requestDate',
requestStatus: 'requestStatus',
lastActionStatus: 'latest_action__status',
lastActionErrorReason: 'latest_action__error_reason',
recentAction: 'latest_action__recent_action',
};

export const applySortByToOptions = (sortBy, options) => {
Expand All @@ -43,27 +46,47 @@ export const applySortByToOptions = (sortBy, options) => {
}
};

// Configuration mapping for filter field transformations
const FILTER_FIELD_CONFIG = {
requestDetails: {
apiParam: 'search',
transform: (value) => value,
},
requestStatus: {
apiParam: 'state',
transform: (value) => (Array.isArray(value) ? value.join(',') : value),
},
lastActionStatus: {
apiParam: 'action_status',
transform: (value) => (Array.isArray(value) ? value.join(',') : value),
},
lastActionErrorReason: {
apiParam: 'action_error_reason',
transform: (value) => (Array.isArray(value) ? value.join(',') : value),
},
};

export const applyFiltersToOptions = (filters, options) => {
if (!filters || filters.length === 0) {
return;
}
const emailSearchQuery = filters.find(filter => filter.id === 'requestDetails')?.value;
const statusFilter = filters.find(filter => filter.id === 'requestStatus')?.value;

if (emailSearchQuery) {
Object.assign(options, {
search: emailSearchQuery,
});
}
filters.forEach(filter => {
const config = FILTER_FIELD_CONFIG[filter.id];

if (statusFilter && statusFilter.length > 0) {
Object.assign(options, {
state: statusFilter.join(','),
});
}
};
if (config && filter.value) {
if (Array.isArray(filter.value) && filter.value.length === 0) {
return;
}
if (typeof filter.value === 'string' && !filter.value.trim()) {
return;
}

const getLastActionStatus = (latestAction) => latestAction?.status?.toLowerCase().replace(/\s+/g, '_');
const transformedValue = config.transform(filter.value);
Object.assign(options, { [config.apiParam]: transformedValue });
}
});
};

// Transform API response data to match DataTable the requirements
const transformApiDataToTableData = (apiResults) => apiResults.map((item) => {
Expand All @@ -85,8 +108,8 @@ const transformApiDataToTableData = (apiResults) => apiResults.map((item) => {
amount: item?.coursePrice || 0,
requestDate,
requestStatus: item?.state,
lastActionStatus: getLastActionStatus(item?.latestAction),
lastActionErrorReason: item?.latestAction?.errorReason,
lastActionStatus: item?.latestAction?.status, // Direct assignment, no transformation
lastActionErrorReason: item?.latestAction?.errorReason, // Use error_reason field
lastActionDate,
latestAction: item?.latestAction,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,24 @@ const BnrRequestStatusCell = ({ row }) => {
const recentAction = latestAction?.recentAction;
const [target, setTarget] = useState(null);
const [isModalOpen, setIsModalOpen] = useState(false);

// Format error reasons for display
const formatErrorReason = (reason) => {
if (!reason) {
return null;
}

const errorReasonMap = {
failed_approval: 'Failed: Approval',
failed_cancellation: 'Failed: Cancellation',
failed_system: 'Failed: System Error',
failed_payment: 'Failed: Payment',
failed_enrollment: 'Failed: Enrollment',
};

return errorReasonMap[reason]
|| reason.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(': ');
};
const getStatusConfig = useMemo(() => {
const statusConfigs = {
requested: {
Expand Down Expand Up @@ -44,7 +62,8 @@ const BnrRequestStatusCell = ({ row }) => {
};

// Determine what to display in the chip
const displayText = errorReason || getStatusConfig.label;
const formattedErrorReason = formatErrorReason(errorReason);
const displayText = formattedErrorReason || getStatusConfig.label;
const displayIcon = errorReason ? Error : getStatusConfig.icon;
const displayVariant = errorReason ? 'dark' : '';
const isClickable = !!errorReason;
Expand All @@ -66,7 +85,7 @@ const BnrRequestStatusCell = ({ row }) => {

{errorReason && (
<RequestFailureModal
errorReason={errorReason}
errorReason={formattedErrorReason}
isOpen={isModalOpen}
onClose={closeModal}
target={target}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1894,12 +1894,12 @@ describe('<BudgetDetailPage />', () => {
const mockRequestsWithDifferentStatuses = [
{
...mockApprovedRequest,
lastActionStatus: 'waiting_for_learner',
lastActionStatus: 'reminded',
},
{
...createMockApprovedRequest(),
lastActionStatus: 'refunded',
lastActionErrorReason: 'Failed: Cancellation',
lastActionErrorReason: 'failed_cancellation',
},
{
...createMockApprovedRequest(),
Expand Down
Loading