From 7aa6c890f28b357303509c5596254bf65ab3bc3f Mon Sep 17 00:00:00 2001 From: Aura Alba Date: Thu, 19 Oct 2023 20:05:03 -0500 Subject: [PATCH 1/2] feat: call service for add instructor --- .../AddInstructors/_test_/index.test.jsx | 6 +++ .../Instructors/AddInstructors/index.jsx | 48 +++++++++++++++++-- .../Instructors/data/_test_/api.test.js | 26 +++++++++- src/features/Instructors/data/api.js | 13 ++++- 4 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/features/Instructors/AddInstructors/_test_/index.test.jsx b/src/features/Instructors/AddInstructors/_test_/index.test.jsx index 3cace2a7..823f08d9 100644 --- a/src/features/Instructors/AddInstructors/_test_/index.test.jsx +++ b/src/features/Instructors/AddInstructors/_test_/index.test.jsx @@ -41,5 +41,11 @@ describe('Add instructor component', () => { const ccxSelect = getByText('Select Class Name'); expect(instructorInfoInput).toBeInTheDocument(); expect(ccxSelect).toBeInTheDocument(); + + fireEvent.change(instructorInfoInput, { target: { value: 'Name' } }); + const submitButton = getByText('Add'); + await act(async () => { + fireEvent.click(submitButton); + }); }); }); diff --git a/src/features/Instructors/AddInstructors/index.jsx b/src/features/Instructors/AddInstructors/index.jsx index 939ed3e9..77216a45 100644 --- a/src/features/Instructors/AddInstructors/index.jsx +++ b/src/features/Instructors/AddInstructors/index.jsx @@ -2,7 +2,7 @@ import React, { useState, useReducer, useEffect } from 'react'; import { Button, FormGroup, ModalDialog, useToggle, Form, } from '@edx/paragon'; -import { getCCXList } from 'features/Instructors/data/api'; +import { getCCXList, handleInstructorsEnrollment } from 'features/Instructors/data/api'; import reducer from 'features/Instructors/AddInstructors/reducer'; import { logError } from '@edx/frontend-platform/logging'; import { camelCaseObject } from '@edx/frontend-platform'; @@ -26,6 +26,8 @@ const AddInstructors = () => { const [state, dispatch] = useReducer(reducer, initialState); const [isOpen, open, close] = useToggle(false); const [addInstructorInfo, setAddInstructorInfo] = useState(addInstructorState); + const [isNoUser, setIsNoUser] = useState(false); + const enrollmentData = new FormData(); const fetchData = async () => { dispatch({ type: FETCH_CCX_LIST_REQUEST }); @@ -46,6 +48,17 @@ const AddInstructors = () => { }); }; + // Set default value + useEffect(() => { + if (state.data.length > 0) { + setAddInstructorInfo((prevState) => ({ + ...prevState, + ccxId: state.data[0].classId, + ccxName: state.data[0].className, + })); + } + }, [state.data]); + const handleCcxSelect = (e) => { const select = e.target; setAddInstructorInfo({ @@ -55,6 +68,24 @@ const AddInstructors = () => { }); }; + const handleAddInstructors = async () => { + try { + enrollmentData.append('unique_student_identifier', addInstructorInfo.instructorInfo); + enrollmentData.append('rolename', 'staff'); + enrollmentData.append('action', 'allow'); + const response = await handleInstructorsEnrollment(enrollmentData, addInstructorInfo.ccxId); + if (response.data?.userDoesNotExist) { + setIsNoUser(true); + } else { + fetchData(); + close(); + setIsNoUser(false); + } + } catch (error) { + logError(error); + } + }; + useEffect(() => { fetchData(); }, []); @@ -83,9 +114,12 @@ const AddInstructors = () => { className="my-4 mr-0" onChange={handleCcxSelect} id="selectCcx" + value={addInstructorInfo.ccxId} > {state.data.map((ccx) => )} + + { onChange={handleInstructorInput} className="my-4 mr-0" /> -
- -
+ {isNoUser && ( + + User does not exist + + )}
+
+ +
+ diff --git a/src/features/Instructors/data/_test_/api.test.js b/src/features/Instructors/data/_test_/api.test.js index 9e4f38c1..70825fdf 100644 --- a/src/features/Instructors/data/_test_/api.test.js +++ b/src/features/Instructors/data/_test_/api.test.js @@ -1,5 +1,5 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; -import { getInstructorData } from 'features/Instructors/data/api'; +import { getInstructorData, handleInstructorsEnrollment } from 'features/Instructors/data/api'; jest.mock('@edx/frontend-platform/auth', () => ({ getAuthenticatedHttpClient: jest.fn(), @@ -34,3 +34,27 @@ describe('getInstructorData', () => { ); }); }); + +describe('handleInstructorsEnrollment', () => { + test('should call getAuthenticatedHttpClient with the correct parameters', () => { + const httpClientMock = { + post: jest.fn(), + }; + + const courseId = 'course123'; + const data = new FormData(); + + getAuthenticatedHttpClient.mockReturnValue(httpClientMock); + + handleInstructorsEnrollment(data, courseId); + + expect(getAuthenticatedHttpClient).toHaveBeenCalledTimes(2); + expect(getAuthenticatedHttpClient).toHaveBeenCalledWith(); + + expect(httpClientMock.post).toHaveBeenCalledTimes(1); + expect(httpClientMock.post).toHaveBeenCalledWith( + 'http://localhost:18000/courses/course123/instructor/api/modify_access', + data, + ); + }); +}); diff --git a/src/features/Instructors/data/api.js b/src/features/Instructors/data/api.js index c87e5bb0..e90ae436 100644 --- a/src/features/Instructors/data/api.js +++ b/src/features/Instructors/data/api.js @@ -17,11 +17,22 @@ function getInstructorData(page, filters) { function getCCXList() { const apiV2BaseUrl = getConfig().COURSE_OPERATIONS_API_V2_BASE_URL; return getAuthenticatedHttpClient().get( - `${apiV2BaseUrl}/classes?limit=false`, + `${apiV2BaseUrl}/classes/?limit=false`, + ); +} + +function handleInstructorsEnrollment(data, courseId) { + const INSTRUCTOR_API_URL = `${getConfig().LMS_BASE_URL}/courses/course_id/instructor/api`; + const courseIdSearchPattern = /course_id/; + + return getAuthenticatedHttpClient().post( + `${INSTRUCTOR_API_URL.replace(courseIdSearchPattern, courseId)}/modify_access`, + data, ); } export { getInstructorData, getCCXList, + handleInstructorsEnrollment, }; From dd8ac998eb825a70f20e31f96de00bf9f92d08a5 Mon Sep 17 00:00:00 2001 From: Aura Alba Date: Mon, 23 Oct 2023 21:25:17 -0500 Subject: [PATCH 2/2] feat: change controlled components form to uncontrolled --- .../Instructors/AddInstructors/index.jsx | 104 +++++++----------- 1 file changed, 37 insertions(+), 67 deletions(-) diff --git a/src/features/Instructors/AddInstructors/index.jsx b/src/features/Instructors/AddInstructors/index.jsx index 77216a45..208172d6 100644 --- a/src/features/Instructors/AddInstructors/index.jsx +++ b/src/features/Instructors/AddInstructors/index.jsx @@ -16,16 +16,9 @@ const initialState = { error: null, }; -const addInstructorState = { - instructorInfo: '', - ccxId: '', - ccxName: '', -}; - const AddInstructors = () => { const [state, dispatch] = useReducer(reducer, initialState); const [isOpen, open, close] = useToggle(false); - const [addInstructorInfo, setAddInstructorInfo] = useState(addInstructorState); const [isNoUser, setIsNoUser] = useState(false); const enrollmentData = new FormData(); @@ -41,39 +34,16 @@ const AddInstructors = () => { } }; - const handleInstructorInput = (e) => { - setAddInstructorInfo({ - ...addInstructorInfo, - instructorInfo: e.target.value.trim(), - }); - }; - - // Set default value - useEffect(() => { - if (state.data.length > 0) { - setAddInstructorInfo((prevState) => ({ - ...prevState, - ccxId: state.data[0].classId, - ccxName: state.data[0].className, - })); - } - }, [state.data]); - - const handleCcxSelect = (e) => { - const select = e.target; - setAddInstructorInfo({ - ...addInstructorInfo, - ccxId: select.value, - ccxName: select.options[select.selectedIndex].text, - }); - }; - - const handleAddInstructors = async () => { + const handleAddInstructors = async (e) => { + e.preventDefault(); + const form = e.target; + const formData = new FormData(form); + const formJson = Object.fromEntries(formData.entries()); try { - enrollmentData.append('unique_student_identifier', addInstructorInfo.instructorInfo); + enrollmentData.append('unique_student_identifier', formJson.instructorInfo); enrollmentData.append('rolename', 'staff'); enrollmentData.append('action', 'allow'); - const response = await handleInstructorsEnrollment(enrollmentData, addInstructorInfo.ccxId); + const response = await handleInstructorsEnrollment(enrollmentData, formJson.ccxId); if (response.data?.userDoesNotExist) { setIsNoUser(true); } else { @@ -107,36 +77,36 @@ const AddInstructors = () => { - - - {state.data.map((ccx) => )} - - - - - {isNoUser && ( - - User does not exist - - )} - -
- -
+
+ + + + {state.data.map((ccx) => )} + + + + + {isNoUser && ( + + User does not exist + + )} + +
+ +
+