Skip to content

Commit

Permalink
feat: change course/class title by key
Browse files Browse the repository at this point in the history
  • Loading branch information
sergivalero20 committed Aug 14, 2024
1 parent 03ec792 commit a81fe7e
Show file tree
Hide file tree
Showing 13 changed files with 123 additions and 90 deletions.
18 changes: 8 additions & 10 deletions src/features/Classes/Class/ClassPage/Actions.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { useParams, useLocation, useHistory } from 'react-router-dom';
import { useParams, useHistory } from 'react-router-dom';
import { getConfig } from '@edx/frontend-platform';

import { Button } from 'react-paragon-topaz';
Expand All @@ -17,17 +17,15 @@ import AddClass from 'features/Courses/AddClass';
import EnrollStudent from 'features/Classes/EnrollStudent';

const Actions = ({ previousPage }) => {
const location = useLocation();
const history = useHistory();
const { courseName, className } = useParams();
const { courseId, classId } = useParams();
const classIdDecoded = decodeURIComponent(classId);
const classes = useSelector((state) => state.classes.allClasses.data);

const queryParams = new URLSearchParams(location.search);
const queryClassId = queryParams.get('classId')?.replaceAll(' ', '+');
const classLink = `${getConfig().LEARNING_MICROFRONTEND_URL}/course/${queryClassId}/home`;
const classLink = `${getConfig().LEARNING_MICROFRONTEND_URL}/course/${classIdDecoded}/home`;

const [classInfo] = classes.filter(
(classElement) => classElement.classId === queryClassId,
(classElement) => classElement.classId === classIdDecoded,
);

const [isOpenEditModal, openEditModal, closeEditModal] = useToggle(false);
Expand All @@ -37,7 +35,7 @@ const Actions = ({ previousPage }) => {
const addQueryParam = useInstitutionIdQueryParam();

const handleManageButton = () => {
history.push(addQueryParam(`/manage-instructors/${courseName}/${className}?classId=${queryClassId}&previous=${previousPage}`));
history.push(addQueryParam(`/manage-instructors/${courseId}/${classId}?previous=${previousPage}`));
};

return (
Expand All @@ -51,7 +49,7 @@ const Actions = ({ previousPage }) => {
</Button>
<Button
as="a"
onClick={() => setAssignStaffRole(classLink, queryClassId)}
onClick={() => setAssignStaffRole(classLink, classIdDecoded)}
className="text-decoration-none text-white button-view-class mr-3"
>
<i className="fa-solid fa-arrow-up-right-from-square mr-2 mb-1" />
Expand Down Expand Up @@ -93,7 +91,7 @@ const Actions = ({ previousPage }) => {
isEditing
isDetailClassPage
/>
<EnrollStudent isOpen={isEnrollModalOpen} onClose={handleEnrollStudentModal} queryClassId={queryClassId} />
<EnrollStudent isOpen={isEnrollModalOpen} onClose={handleEnrollStudentModal} />
</>
);
};
Expand Down
36 changes: 24 additions & 12 deletions src/features/Classes/Class/ClassPage/index.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import React, { useState, useEffect, useRef } from 'react';
import React, {
useState,
useEffect,
useRef,
useMemo,
} from 'react';
import { useParams, useHistory, useLocation } from 'react-router-dom';
import { Container, Pagination } from '@edx/paragon';
import { useDispatch, useSelector } from 'react-redux';
Expand All @@ -25,11 +30,11 @@ const ClassPage = () => {
const location = useLocation();
const history = useHistory();
const dispatch = useDispatch();
const { courseName, className } = useParams();
const { courseId, classId } = useParams();
const queryParams = new URLSearchParams(location.search);
const previousPage = queryParams.get('previous') || 'classes';
const courseNameDecoded = decodeURIComponent(courseName);
const classNameDecoded = decodeURIComponent(className);
const courseIdDecoded = decodeURIComponent(courseId);
const classIdDecoded = decodeURIComponent(classId);

const institutionRef = useRef(undefined);
const [currentPage, setCurrentPage] = useState(initialPage);
Expand All @@ -44,23 +49,30 @@ const ClassPage = () => {
dispatch(updateCurrentPage(targetPage));
};

const defaultClassInfo = useMemo(() => ({
className: '',
}), []);

const classInfo = useSelector((state) => state.classes.allClasses.data)
.find((classElement) => classElement?.classId === classIdDecoded) || defaultClassInfo;

useEffect(() => {
const initialTitle = document.title;

document.title = classNameDecoded;
document.title = classIdDecoded;
// Leaves a gap time space to prevent being override by ActiveTabUpdater component
setTimeout(() => dispatch(updateActiveTab(previousPage)), 100);

return () => {
document.title = initialTitle;
};
}, [dispatch, classNameDecoded, previousPage]);
}, [dispatch, classIdDecoded, previousPage]);

useEffect(() => {
if (institution.id) {
const params = {
course_name: courseNameDecoded,
class_name: classNameDecoded,
course_id: courseIdDecoded,
class_id: classIdDecoded,
limit: true,
};

Expand All @@ -71,18 +83,18 @@ const ClassPage = () => {
dispatch(resetStudentsTable());
dispatch(updateCurrentPage(initialPage));
};
}, [dispatch, institution.id, courseNameDecoded, classNameDecoded, currentPage]);
}, [dispatch, institution.id, courseIdDecoded, classIdDecoded, currentPage]);

useEffect(() => {
if (institution.id) {
dispatch(fetchAllClassesData(institution.id, courseNameDecoded));
dispatch(fetchAllClassesData(institution.id, courseIdDecoded));
}

return () => {
dispatch(resetClassesTable());
dispatch(resetClasses());
};
}, [dispatch, institution.id, courseNameDecoded]);
}, [dispatch, institution.id, courseIdDecoded]);

useEffect(() => {
if (institution.id !== undefined && institutionRef.current === undefined) {
Expand All @@ -102,7 +114,7 @@ const ClassPage = () => {
<Button onClick={() => history.goBack()} className="mr-3 link back-arrow" variant="tertiary">
<i className="fa-solid fa-arrow-left" />
</Button>
<h3 className="h2 mb-0 course-title">Class details: {classNameDecoded}</h3>
<h3 className="h2 mb-0 course-title">Class details: {classInfo.className}</h3>
</div>
</div>

Expand Down
2 changes: 1 addition & 1 deletion src/features/Classes/ClassesTable/columns.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const columns = [
accessor: 'className',
Cell: ({ row }) => (
<LinkWithQuery
to={`/courses/${encodeURIComponent(row.original.masterCourseName)}/${encodeURIComponent(row.values.className)}?classId=${row.original.classId}&previous=classes`}
to={`/courses/${encodeURIComponent(row.original.masterCourseId)}/${encodeURIComponent(row.original.classId)}?previous=classes`}
className="text-truncate link"
>
{row.values.className}
Expand Down
28 changes: 17 additions & 11 deletions src/features/Classes/EnrollStudent/index.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useMemo } from 'react';
import { useParams } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux';
import PropTypes from 'prop-types';
Expand All @@ -21,16 +21,23 @@ import { initialPage } from 'features/constants';

import 'features/Classes/EnrollStudent/index.scss';

const EnrollStudent = ({ isOpen, onClose, queryClassId }) => {
const EnrollStudent = ({ isOpen, onClose }) => {
const dispatch = useDispatch();

const { courseName, className } = useParams();
const { courseId, classId } = useParams();
const [showToast, setShowToast] = useState(false);
const [isLoading, setLoading] = useState(false);
const [toastMessage, setToastMessage] = useState('');
const institution = useSelector((state) => state.main.selectedInstitution);
const courseNameDecoded = decodeURIComponent(courseName);
const classNameDecoded = decodeURIComponent(className);
const courseIdDecoded = decodeURIComponent(courseId);
const classIdDecoded = decodeURIComponent(classId);

const defaultClassInfo = useMemo(() => ({
className: '',
}), []);

const classInfo = useSelector((state) => state.classes.allClasses.data)
.find((classElement) => classElement?.classId === classIdDecoded) || defaultClassInfo;

const handleEnrollStudent = async (e) => {
e.preventDefault();
Expand All @@ -48,7 +55,7 @@ const EnrollStudent = ({ isOpen, onClose, queryClassId }) => {

try {
setLoading(true);
const response = await handleEnrollments(formData, queryClassId);
const response = await handleEnrollments(formData, classIdDecoded);
const validationEmailList = response?.data?.results;
const messages = await getMessages();
const validEmails = [];
Expand Down Expand Up @@ -85,15 +92,15 @@ const EnrollStudent = ({ isOpen, onClose, queryClassId }) => {
setToastMessage(textToast);

const params = {
course_name: courseNameDecoded,
class_name: classNameDecoded,
course_id: courseIdDecoded,
class_id: classIdDecoded,
limit: true,
};

dispatch(fetchStudentsData(institution.id, initialPage, params));

// Get the classes info updated with the new number of students enrolled.
dispatch(fetchAllClassesData(institution.id, courseNameDecoded));
dispatch(fetchAllClassesData(institution.id, courseIdDecoded));

setShowToast(true);
return onClose();
Expand Down Expand Up @@ -125,7 +132,7 @@ const EnrollStudent = ({ isOpen, onClose, queryClassId }) => {
</ModalDialog.Header>
<ModalDialog.Body className="body-container h-100">
<p className="text-uppercase font-weight-bold sub-title">
Class: {classNameDecoded}
Class: {classInfo.className}
</p>
{isLoading && (
<div className="w-100 h-100 d-flex justify-content-center align-items-center">
Expand Down Expand Up @@ -166,7 +173,6 @@ const EnrollStudent = ({ isOpen, onClose, queryClassId }) => {
EnrollStudent.propTypes = {
isOpen: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
queryClassId: PropTypes.string.isRequired,
};

export default EnrollStudent;
25 changes: 10 additions & 15 deletions src/features/Classes/InstructorCard/index.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useParams, useLocation } from 'react-router-dom';
import { useParams } from 'react-router-dom';

import { formatDateRange } from 'helpers';
import { initialPage, RequestStatus } from 'features/constants';
Expand All @@ -15,39 +15,34 @@ import 'features/Classes/InstructorCard/index.scss';
const INSTRUCTORS_NUMBER = 3;

const InstructorCard = () => {
const location = useLocation();
const dispatch = useDispatch();
const { courseName, className } = useParams();
const { classId } = useParams();
const institution = useSelector((state) => state.main.selectedInstitution);
const instructors = useSelector((state) => state.instructors.selectOptions.data);
const classes = useSelector((state) => state.classes.allClasses);
const courseNameDecoded = decodeURIComponent(courseName);
const classNameDecoded = decodeURIComponent(className);

const queryParams = new URLSearchParams(location.search);
const classIdQuery = queryParams.get('classId')?.replaceAll(' ', '+');
const classIdDecoded = decodeURIComponent(classId);

const isLoadingClasses = classes.status === RequestStatus.LOADING;

const [classInfo] = classes.data.filter(
(classElement) => classElement.classId === classIdQuery,
(classElement) => classElement.classId === classIdDecoded,
);

const totalEnrolled = (classInfo?.numberOfStudents || 0)
+ (classInfo?.numberOfPendingStudents || 0);

useEffect(() => {
if (institution.id) {
dispatch(fetchInstructorsOptionsData(institution.id, initialPage, { limit: false, class_id: classIdQuery }));
dispatch(fetchInstructorsOptionsData(institution.id, initialPage, { limit: false, class_id: classIdDecoded }));
}
return () => dispatch(resetInstructorOptions());
}, [institution.id, classIdQuery, dispatch]);
}, [institution.id, classIdDecoded, dispatch]);

return (
<article className="instructor-wrapper mb-4 d-flex flex-column flex-sm-row justify-content-between align-items-start">
<div className="d-flex flex-column w-75 justify-content-between h-100">
<h3 className="text-color text-uppercase font-weight-bold text-truncate w-75" title={classNameDecoded}>
{classNameDecoded}
<h3 className="text-color text-uppercase font-weight-bold text-truncate w-75" title={classInfo?.className}>
{classInfo?.className}
</h3>
{isLoadingClasses && (
<div className="w-100 h-100 d-flex justify-content-center align-items-center">
Expand All @@ -60,8 +55,8 @@ const InstructorCard = () => {
)}
{!isLoadingClasses && (
<>
<h4 className="text-color text-uppercase font-weight-bold text-truncate w-75" title={courseNameDecoded}>
{courseNameDecoded}
<h4 className="text-color text-uppercase font-weight-bold text-truncate w-75" title={classInfo?.masterCourseName}>
{classInfo?.masterCourseName}
</h4>
<div className="text-uppercase">
<i className="fa-regular fa-calendar mr-2" />
Expand Down
12 changes: 6 additions & 6 deletions src/features/Classes/data/thunks.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ import {
import { getClassesByInstitution } from 'features/Common/data/api';
import { initialPage } from 'features/constants';

function fetchClassesData(id, currentPage, courseName = '', urlParamsFilters = '', limit = true) {
function fetchClassesData(id, currentPage, courseId = '', urlParamsFilters = '', limit = true) {
return async (dispatch) => {
dispatch(fetchClassesDataRequest());

try {
const response = camelCaseObject(
await getClassesByInstitution(id, courseName, limit, currentPage, urlParamsFilters),
await getClassesByInstitution(id, courseId, limit, currentPage, urlParamsFilters),
);
dispatch(fetchClassesDataSuccess(response.data));
} catch (error) {
Expand All @@ -29,12 +29,12 @@ function fetchClassesData(id, currentPage, courseName = '', urlParamsFilters = '
};
}

function fetchClassesOptionsData(id, courseName) {
function fetchClassesOptionsData(id, courseId) {
return async (dispatch) => {
dispatch(fetchClassesDataRequest());

try {
const response = camelCaseObject(await getClassesByInstitution(id, courseName, false));
const response = camelCaseObject(await getClassesByInstitution(id, courseId, false));
dispatch(updateClassesOptions(response.data));
} catch (error) {
dispatch(fetchClassesDataFailed());
Expand All @@ -43,13 +43,13 @@ function fetchClassesOptionsData(id, courseName) {
};
}

function fetchAllClassesData(id, courseName = '', urlParamsFilters = '', limit = false) {
function fetchAllClassesData(id, courseId = '', urlParamsFilters = '', limit = false) {
return async (dispatch) => {
dispatch(fetchAllClassesDataRequest());

try {
const response = camelCaseObject(
await getClassesByInstitution(id, courseName, limit, initialPage, urlParamsFilters),
await getClassesByInstitution(id, courseId, limit, initialPage, urlParamsFilters),
);
dispatch(fetchAllClassesDataSuccess(response.data));
} catch (error) {
Expand Down
6 changes: 3 additions & 3 deletions src/features/Common/data/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ function getLicensesByInstitution(institutionId, limit, page = 1, urlParamsFilte
);
}

function getClassesByInstitution(institutionId, courseName, limit = false, page = '', urlParamsFilters = '') {
const encodedCourseName = encodeURIComponent(courseName);
function getClassesByInstitution(institutionId, courseId, limit = false, page = '', urlParamsFilters = '') {
const encodedCourseId = encodeURIComponent(courseId);
const params = {
limit,
institution_id: institutionId,
page,
...urlParamsFilters,
};
return getAuthenticatedHttpClient().get(
`${getConfig().COURSE_OPERATIONS_API_V2_BASE_URL}/classes/?course_name=${encodedCourseName}`,
`${getConfig().COURSE_OPERATIONS_API_V2_BASE_URL}/classes/?course_id=${encodedCourseId}`,
{ params },
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/features/Courses/CourseDetailTable/columns.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ const columns = [
Header: 'Class',
accessor: 'className',
Cell: ({ row }) => {
const { courseName } = useParams();
const { courseId } = useParams();

return (
<LinkWithQuery
to={`/courses/${courseName}/${encodeURIComponent(row.values.className)}?classId=${row.original.classId}&previous=courses`}
to={`/courses/${courseId}/${encodeURIComponent(row.original.classId)}?previous=courses`}
className="text-truncate link"
>
{row.values.className}
Expand Down
Loading

0 comments on commit a81fe7e

Please sign in to comment.