Skip to content
Merged
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 @@ -16,8 +16,7 @@ const DeclineBnrSubsidyRequestModal = ({
onSuccess,
onClose,
}) => {
const [shouldNotifyLearner, setShouldNotifyLearner] = useState(true);
const [shouldUnlinkLearnerFromEnterprise, setShouldUnlinkLearnerFromEnterprise] = useState(false);
const [declineReason, setDeclineReason] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState();

Expand All @@ -40,8 +39,8 @@ const DeclineBnrSubsidyRequestModal = ({
await declineRequestFn({
enterpriseId,
subsidyRequestUUID: uuid,
sendNotification: shouldNotifyLearner,
unlinkUsersFromEnterprise: shouldUnlinkLearnerFromEnterprise,
sendNotification: true, // always true as it would be by default
declineReason, // include the reason field
});
onSuccess();
} catch (err) {
Expand All @@ -51,8 +50,7 @@ const DeclineBnrSubsidyRequestModal = ({
}
}, [
uuid,
shouldNotifyLearner,
shouldUnlinkLearnerFromEnterprise,
declineReason,
declineRequestFn,
onSuccess,
enterpriseId,
Expand Down Expand Up @@ -87,23 +85,22 @@ const DeclineBnrSubsidyRequestModal = ({
Declining an enrollment request cannot be undone. If you change your mind, the learner will have to
submit a new enrollment request.
</p>
<Form.Checkbox
className="py-3"
data-testid="decline-bnr-subsidy-request-modal-notify-learner-checkbox"
checked={shouldNotifyLearner}
onChange={(e) => setShouldNotifyLearner(e.target.checked)}
>
Send the learner an email notification
</Form.Checkbox>
<Form.Checkbox
className="mt-1"
data-testid="decline-subsidy-request-modal-unlink-learner-checkbox"
checked={shouldUnlinkLearnerFromEnterprise}
onChange={(e) => setShouldUnlinkLearnerFromEnterprise(e.target.checked)}
description="Your learner won't know they have been disassociated."
>
Disassociate the learner with your organization
</Form.Checkbox>
{/* Reason input with character count + 250 limit */}
<Form.Group className="mt-3">
<Form.Control
as="textarea"
rows={2}
maxLength={250} // Max Character limit
placeholder="Reason for declining"
data-testid="decline-subsidy-request-reason-input"
value={declineReason}
onChange={(e) => setDeclineReason(e.target.value)}
/>
<div className="text-right text-muted mt-1" style={{ fontSize: '0.85rem' }}>
{declineReason.length}/250 characters
</div>
</Form.Group>

</ModalDialog.Body>
<ModalDialog.Footer>
<ActionRow>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,10 +343,21 @@ describe('BudgetDetailRequestsTabContent', () => {
expect(screen.getByText(/Declining an enrollment request cannot be undone/)).toBeInTheDocument();
expect(screen.getByTestId('decline-subsidy-request-modal-decline-btn')).toBeInTheDocument();
expect(screen.getByTestId('decline-subsidy-request-modal-close-btn')).toBeInTheDocument();
expect(screen.getByTestId('decline-bnr-subsidy-request-modal-notify-learner-checkbox')).toBeInTheDocument();
expect(screen.getByTestId('decline-subsidy-request-modal-unlink-learner-checkbox')).toBeInTheDocument();
});
});
it('should allow the user to enter a decline reason in the modal', async () => {
const user = userEvent.setup();
render(<BudgetDetailRequestsTabContentWrapper />);
await waitFor(() => {
expect(screen.getByRole('table')).toBeInTheDocument();
});
const declineButton = screen.getByRole('button', { name: /decline/i });
await user.click(declineButton);
const reasonInput = await screen.findByTestId('decline-subsidy-request-reason-input');
await user.type(reasonInput, 'This course is not approved.');
expect(reasonInput).toHaveValue('This course is not approved.');
expect(screen.getByText(/28\/250/i)).toBeInTheDocument();
});

it('should call decline API with default options and refresh requests on successful decline', async () => {
const user = userEvent.setup();
Expand Down Expand Up @@ -381,14 +392,14 @@ describe('BudgetDetailRequestsTabContent', () => {
enterpriseId: 'test-enterprise-id',
subsidyRequestUUID: 'request-1',
sendNotification: true,
unlinkUsersFromEnterprise: false,
declineReason: '',
});
expect(mockRefreshRequests).toHaveBeenCalledTimes(1);
expect(screen.queryByText('Are you sure?')).not.toBeInTheDocument();
});
});

it('should call decline API with custom options when checkboxes are modified', async () => {
it('should call decline API with default options', async () => {
const user = userEvent.setup();
const mockDeclineRequest = jest.fn().mockResolvedValue({});

Expand All @@ -403,25 +414,15 @@ describe('BudgetDetailRequestsTabContent', () => {
const declineButton = screen.getByRole('button', { name: /decline/i });
await user.click(declineButton);

await waitFor(() => {
expect(screen.getByTestId('decline-bnr-subsidy-request-modal-notify-learner-checkbox')).toBeInTheDocument();
});

const notifyCheckbox = screen.getByTestId('decline-bnr-subsidy-request-modal-notify-learner-checkbox');
const unlinkCheckbox = screen.getByTestId('decline-subsidy-request-modal-unlink-learner-checkbox');

await user.click(notifyCheckbox);
await user.click(unlinkCheckbox);

const confirmButton = screen.getByTestId('decline-subsidy-request-modal-decline-btn');
await user.click(confirmButton);

await waitFor(() => {
expect(mockDeclineRequest).toHaveBeenCalledWith({
enterpriseId: 'test-enterprise-id',
subsidyRequestUUID: 'request-1',
sendNotification: false,
unlinkUsersFromEnterprise: true,
sendNotification: true, // always true now as it would be sent by default
declineReason: '', // default empty
});
});
});
Expand Down
6 changes: 3 additions & 3 deletions src/data/services/EnterpriseAccessApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,20 +336,20 @@ class EnterpriseAccessApiService {
* @param params.enterpriseId - The UUID of the enterprise customer
* @param params.subsidyRequestUUID - The UUID of the subsidy request to decline
* @param params.sendNotification - Whether to send a notification about the decline
* @param params.unlinkUsersFromEnterprise - Whether to disassociate users from the organization
* @param params.declineReason - The reason for declining
* @returns A promise that resolves to the API response for the decline operation
*/
static declineBnrSubsidyRequest({
enterpriseId,
subsidyRequestUUID,
sendNotification,
unlinkUsersFromEnterprise,
declineReason,
}) {
const options = {
subsidy_request_uuid: subsidyRequestUUID,
enterprise_customer_uuid: enterpriseId,
send_notification: sendNotification,
disassociate_from_org: unlinkUsersFromEnterprise,
decline_reason: declineReason,
};

const url = `${EnterpriseAccessApiService.baseUrl}/learner-credit-requests/decline/`;
Expand Down
22 changes: 2 additions & 20 deletions src/data/services/tests/EnterpriseAccessApiService.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -322,32 +322,14 @@ describe('EnterpriseAccessApiService', () => {
enterpriseId: mockEnterpriseUUID,
subsidyRequestUUID: mockBnrSubsidyRequestUUID,
sendNotification: true,
unlinkUsersFromEnterprise: false,
declineReason: '',
});

expect(axios.post).toBeCalledWith(`${enterpriseAccessBaseUrl}/api/v1/learner-credit-requests/decline/`, {
subsidy_request_uuid: mockBnrSubsidyRequestUUID,
enterprise_customer_uuid: mockEnterpriseUUID,
send_notification: true,
disassociate_from_org: false,
});
});

test('declineBnrSubsidyRequest calls enterprise-access with unlinkUsersFromEnterprise set to true', () => {
const mockBnrSubsidyRequestUUID = 'test-bnr-subsidy-request-uuid';

EnterpriseAccessApiService.declineBnrSubsidyRequest({
enterpriseId: mockEnterpriseUUID,
subsidyRequestUUID: mockBnrSubsidyRequestUUID,
sendNotification: false,
unlinkUsersFromEnterprise: true,
});

expect(axios.post).toBeCalledWith(`${enterpriseAccessBaseUrl}/api/v1/learner-credit-requests/decline/`, {
subsidy_request_uuid: mockBnrSubsidyRequestUUID,
enterprise_customer_uuid: mockEnterpriseUUID,
send_notification: false,
disassociate_from_org: true,
decline_reason: '',
});
});

Expand Down