diff --git a/frontend/src/api/admin_api.js b/frontend/src/api/admin_api.js index 696c45b0..9bdbe87e 100644 --- a/frontend/src/api/admin_api.js +++ b/frontend/src/api/admin_api.js @@ -1,136 +1,161 @@ -import axios from "axios"; +import axios from 'axios'; let all_courses = async () => { - let token = sessionStorage.getItem("token"); + let token = sessionStorage.getItem('token'); - let config = { - headers: { Authorization: `Bearer ${token}` }, - }; + let config = { + headers: { Authorization: `Bearer ${token}` } + }; - try { - return await axios.get(process.env.REACT_APP_API_URL + "/admin/course/all", config); - } catch (err) { - return err.response; - } -} + try { + return await axios.get(process.env.REACT_APP_API_URL + '/admin/course/all', config); + } catch (err) { + return err.response; + } +}; let impersonate = async (username) => { - let token = sessionStorage.getItem("token"); + let token = sessionStorage.getItem('token'); - const data = { - username: username, - } + const data = { + username: username + }; - let config = { - headers: { Authorization: `Bearer ${token}` } - }; - - try { - return await axios.post(process.env.REACT_APP_API_URL + "/admin/impersonate", data, config); - } catch (err) { - return err.response; - } -} + let config = { + headers: { Authorization: `Bearer ${token}` } + }; + try { + return await axios.post(process.env.REACT_APP_API_URL + '/admin/impersonate', data, config); + } catch (err) { + return err.response; + } +}; let add_course = async (data) => { - let token = sessionStorage.getItem("token"); - - let config = { - headers: { Authorization: `Bearer ${token}` }, - }; - - // let data = { - // course_code: course_code, - // course_session: course_session, - // gitlab_group_id: gitlab_group_id, - // default_token_count: default_token_count, - // token_length: token_length, - // hidden: hidden - // } - - try { - return await axios.post(process.env.REACT_APP_API_URL + "/admin/course/add", data, config); - } catch (err) { - return err.response; - } -} + let token = sessionStorage.getItem('token'); + + let config = { + headers: { Authorization: `Bearer ${token}` } + }; + + // let data = { + // course_code: course_code, + // course_session: course_session, + // gitlab_group_id: gitlab_group_id, + // default_token_count: default_token_count, + // token_length: token_length, + // hidden: hidden + // } + + try { + return await axios.post(process.env.REACT_APP_API_URL + '/admin/course/add', data, config); + } catch (err) { + return err.response; + } +}; let change_course = async (data) => { - let token = sessionStorage.getItem("token"); - - let config = { - headers: { Authorization: `Bearer ${token}` }, - }; - - // let data = { - // course_id: course_id, - // course_code: course_code, - // course_session: course_session, - // gitlab_group_id: gitlab_group_id, - // default_token_count: default_token_count, - // token_length: token_length, - // hidden: hidden - // } - - try { - return await axios.put(process.env.REACT_APP_API_URL + "/admin/course/change", data, config); - } catch (err) { - return err.response; - } -} + let token = sessionStorage.getItem('token'); + + let config = { + headers: { Authorization: `Bearer ${token}` } + }; + + // let data = { + // course_id: course_id, + // course_code: course_code, + // course_session: course_session, + // gitlab_group_id: gitlab_group_id, + // default_token_count: default_token_count, + // token_length: token_length, + // hidden: hidden + // } + + try { + return await axios.put( + process.env.REACT_APP_API_URL + '/admin/course/change', + data, + config + ); + } catch (err) { + return err.response; + } +}; let get_role = async (username) => { - let token = sessionStorage.getItem("token"); - - let config = { - headers: { Authorization: `Bearer ${token}` }, - } - - try { - return await axios.get(process.env.REACT_APP_API_URL + "/admin/role/get?username=" + username, config); - } catch (err) { - return err.response; - } -} + let token = sessionStorage.getItem('token'); + + let config = { + headers: { Authorization: `Bearer ${token}` } + }; + + try { + return await axios.get( + process.env.REACT_APP_API_URL + '/admin/role/get?username=' + username, + config + ); + } catch (err) { + return err.response; + } +}; let add_role = async (data) => { - let token = sessionStorage.getItem("token"); - - let config = { - headers: { Authorization: `Bearer ${token}` }, - } - - try { - return await axios.post(process.env.REACT_APP_API_URL + "/admin/role/add", data, config); - } catch (err) { - return err.response; - } -} + let token = sessionStorage.getItem('token'); + + let config = { + headers: { Authorization: `Bearer ${token}` } + }; + + try { + return await axios.post(process.env.REACT_APP_API_URL + '/admin/role/add', data, config); + } catch (err) { + return err.response; + } +}; + +let upload_role = async (formData) => { + let token = sessionStorage.getItem('token'); + + let config = { + headers: { Authorization: `Bearer ${token}` } + }; + + try { + return await axios.post( + `${process.env.REACT_APP_API_URL}/admin/role/upload`, + formData, + config + ); + } catch (err) { + return err.response; + } +}; let delete_role = async (data) => { - let token = sessionStorage.getItem("token"); + let token = sessionStorage.getItem('token'); - let config = { - headers: { Authorization: `Bearer ${token}` }, - data: data - } + let config = { + headers: { Authorization: `Bearer ${token}` }, + data: data + }; - try { - return await axios.delete(process.env.REACT_APP_API_URL + "/admin/role/delete", config); - } catch (err) { - return err.response; - } -} + try { + return await axios.delete(process.env.REACT_APP_API_URL + '/admin/role/delete', config); + } catch (err) { + return err.response; + } +}; let AdminApi = { - all_courses, - add_course, - change_course, - get_role, - add_role, - delete_role, - impersonate -} - -export default AdminApi; \ No newline at end of file + all_courses, + add_course, + change_course, + get_role, + add_role, + upload_role, + delete_role, + impersonate +}; + +export default AdminApi; diff --git a/frontend/src/components/General/AdminCourseCRUD/AdminCourseCRUD.jsx b/frontend/src/components/General/AdminCourseCRUD/AdminCourseCRUD.jsx index a7332b30..25590ecc 100644 --- a/frontend/src/components/General/AdminCourseCRUD/AdminCourseCRUD.jsx +++ b/frontend/src/components/General/AdminCourseCRUD/AdminCourseCRUD.jsx @@ -19,6 +19,9 @@ import CustomTextField from '../../FlexyMainComponents/forms/custom-elements/Cus import CustomFormLabel from '../../FlexyMainComponents/forms/custom-elements/CustomFormLabel'; import CustomSelect from '../../FlexyMainComponents/forms/custom-elements/CustomSelect'; +// Globally change maxWidth for each tab-list component +const MAX_WIDTH = 300; + const AdminCourseCRUD = (props) => { const { onSubmitFunctions } = props; const { AddRole, UploadRoles, DeleteRole } = onSubmitFunctions; @@ -33,7 +36,8 @@ const AdminCourseCRUD = (props) => { control: controlUploads, handleSubmit: handleSubmitUploads, register: registerUploads, - formState: formStateUploads + formState: formStateUploads, + watch: watchUploads } = useForm(); const { control: controlDelete, @@ -45,8 +49,18 @@ const AdminCourseCRUD = (props) => { // Determine if current user to be added is a new user const [isNewUser, setIsNewUser] = React.useState(true); + // Determine whether to delete all users from DB const [deleteAllUsers, setDeleteAllUsers] = React.useState(false); + // For updating filename on uploading file + const [filename, setFilename] = React.useState(''); + + // Watch for filename within 'file' property + const selectedFile = watchUploads('file', ''); + React.useEffect(() => { + if (selectedFile.length > 0) setFilename(selectedFile[0].name); + }, [selectedFile]); + React.useEffect(() => { if ( Object.keys(formStateAdd.errors).length > 0 || @@ -73,7 +87,7 @@ const AdminCourseCRUD = (props) => { flexDirection: 'column', alignItems: 'center' }} - maxWidth={300} + maxWidth={MAX_WIDTH} > { }; const UploadRolesComponent = () => { - return <>; + return ( + + + + + + Acceptable File Extensions: *.csv +
+ Refer to the "Notes" section for the format of file. +
+ + + + File Uploaded: {filename} + + + ( + <> + + Select Role for Users in File * + + + + Instructor + + + TA + + + Student + + + + )} + name="role" + control={controlUploads} + defaultValue="" + rules={{ required: true }} + /> + +
+
+
+ + + Notes: + +
    +
  • + + Precondition: +
    + Users in the file need to already exist in the database. +
    +
  • +
  • + + The format of the csv file being uploaded should be as follows: +
    + + username,email +
    + user1,user1@example.com +
    + user2,user2@example.com +
    + ... +
    +
    + Note that the email field may be left blank and is optional. i.e. + the csv file format can be +
    + + username,email +
    + user1,user1@example.com +
    + user2, +
    + user3,user3@example.com +
    + ... +
    +
    +
  • +
  • + + There is no requirement on the name of the file. + +
  • +
+
+
+ ); }; const DeleteRoleComponent = () => { @@ -278,7 +429,7 @@ const AdminCourseCRUD = (props) => { flexDirection: 'column', alignItems: 'center' }} - maxWidth={300} + maxWidth={MAX_WIDTH} > { }; const uploadRole = (data) => { - AdminApi.add_role({ ...data, course_id: course_id }).then((response) => { + // Create FormData object to send to endpoint + const formData = new FormData(); + if (data['file'][0] !== undefined) { + formData.append('file', data['file'][0]); + } + if (data['role'] !== undefined) { + formData.append('role', data['role']); + } + formData.append('course_id', course_id); + formData.append('update_user_info', false); + + AdminApi.upload_role(formData).then((response) => { console.log(response); - toast(response.data.message); + if (response.status === 200) + toast.success('The users in file have been assigned the specified role', { + theme: 'colored' + }); + else { + toast.error(response.data.message, { theme: 'colored' }); + } }); };