Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions src/studio-home/__mocks__/studioHomeMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = {
rerunLink: '/course_rerun/course-v1:MachineLearning+123+2023',
run: '2023',
url: '/course/course-v1:MachineLearning+123+2023',
cmsLink: '//localhost:18010/courses/course-v1:MachineLearning+123+2023',
},
{
courseKey: 'course-v1:Design+123+e.g.2025',
Expand All @@ -22,6 +23,7 @@ module.exports = {
rerunLink: '/course_rerun/course-v1:Design+123+e.g.2025',
run: 'e.g.2025',
url: '/course/course-v1:Design+123+e.g.2025',
cmsLink: '//localhost:18010/courses/course-v1:Design+123+e.g.2025',
},
],
canCreateOrganizations: true,
Expand Down
8 changes: 6 additions & 2 deletions src/studio-home/card-item/CardItem.test.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { render } from '@testing-library/react';
import { render, fireEvent } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppProvider } from '@edx/frontend-platform/react';
import { initializeMockApp, getConfig } from '@edx/frontend-platform';
Expand Down Expand Up @@ -45,13 +45,17 @@ describe('<CardItem />', () => {
});
it('should render correct links for non-library course', () => {
const props = studioHomeMock.archivedCourses[0];
const { getByText } = render(<RootWrapper {...props} />);
const { getByText, getByTestId } = render(<RootWrapper {...props} />);
const courseTitleLink = getByText(props.displayName);
expect(courseTitleLink).toHaveAttribute('href', `${getConfig().STUDIO_BASE_URL}${props.url}`);
const dropDownMenu = getByTestId('toggle-dropdown');
fireEvent.click(dropDownMenu);
const btnReRunCourse = getByText(messages.btnReRunText.defaultMessage);
expect(btnReRunCourse).toHaveAttribute('href', props.rerunLink);
const viewLiveLink = getByText(messages.viewLiveBtnText.defaultMessage);
expect(viewLiveLink).toHaveAttribute('href', props.lmsLink);
const editInStudioLink = getByText(messages.editStudioBtnText.defaultMessage);
expect(editInStudioLink).toHaveAttribute('href', props.cmsLink);
});
Comment thread
johnvente marked this conversation as resolved.
it('should render course details for library course', () => {
const props = { ...studioHomeMock.archivedCourses[0], isLibraries: true };
Expand Down
54 changes: 42 additions & 12 deletions src/studio-home/card-item/index.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import React from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { ActionRow, Card, Hyperlink } from '@edx/paragon';
import {
Card,
Hyperlink,
Dropdown,
IconButton,
} from '@edx/paragon';
import { MoreHoriz } from '@edx/paragon/icons';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';

Expand All @@ -10,7 +16,16 @@ import { getStudioHomeData } from '../data/selectors';
import messages from '../messages';

const CardItem = ({
intl, displayName, lmsLink, rerunLink, org, number, run, isLibraries, url,
intl,
displayName,
lmsLink,
rerunLink,
org,
number,
run,
isLibraries,
url,
cmsLink,
}) => {
const {
allowCourseReruns,
Expand Down Expand Up @@ -41,16 +56,29 @@ const CardItem = ({
)}
subtitle={subtitle}
actions={showActions && (
<ActionRow>
{isShowRerunLink && (
<Hyperlink className="small" destination={rerunLink}>
{intl.formatMessage(messages.btnReRunText)}
</Hyperlink>
)}
<Hyperlink className="small ml-3" destination={lmsLink}>
{intl.formatMessage(messages.viewLiveBtnText)}
</Hyperlink>
</ActionRow>
<Dropdown>
<Dropdown.Toggle
as={IconButton}
iconAs={MoreHoriz}
variant="primary"
data-testid="toggle-dropdown"
/>
<Dropdown.Menu>
{isShowRerunLink && (
<Dropdown.Item href={rerunLink}>
{messages.btnReRunText.defaultMessage}
</Dropdown.Item>
)}
<Dropdown.Item href={lmsLink}>
{intl.formatMessage(messages.viewLiveBtnText)}
</Dropdown.Item>

Comment thread
johnvente marked this conversation as resolved.
Outdated
<Dropdown.Item href={cmsLink}>
{intl.formatMessage(messages.editStudioBtnText)}
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>

Comment thread
johnvente marked this conversation as resolved.
Outdated
)}
/>
</Card>
Expand All @@ -62,12 +90,14 @@ CardItem.defaultProps = {
rerunLink: '',
lmsLink: '',
run: '',
cmsLink: '',
};

CardItem.propTypes = {
intl: intlShape.isRequired,
displayName: PropTypes.string.isRequired,
lmsLink: PropTypes.string,
cmsLink: PropTypes.string,
rerunLink: PropTypes.string,
org: PropTypes.string.isRequired,
run: PropTypes.string,
Expand Down
4 changes: 2 additions & 2 deletions src/studio-home/data/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export async function getStudioHomeData() {
return camelCaseObject(data);
}

export async function getStudioHomeCourses(search) {
const { data } = await getAuthenticatedHttpClient().get(`${getApiBaseUrl()}/api/contentstore/v1/home/courses${search}`);
export async function getStudioHomeCourses(search, customParams) {
const { data } = await getAuthenticatedHttpClient().get(`${getApiBaseUrl()}/api/contentstore/v2/home/courses${search}`, { params: customParams });
Comment thread
johnvente marked this conversation as resolved.
Outdated
return camelCaseObject(data);
}

Expand Down
2 changes: 1 addition & 1 deletion src/studio-home/data/api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('studio-home api calls', () => {
});

fit('should get studio courses data', async () => {
const apiLink = `${getApiBaseUrl()}/api/contentstore/v1/home/courses`;
const apiLink = `${getApiBaseUrl()}/api/contentstore/v2/home/courses`;
axiosMock.onGet(apiLink).reply(200, generateGetStudioCoursesApiResponse());
const result = await getStudioHomeCourses('');
const expected = generateGetStudioCoursesApiResponse();
Expand Down
1 change: 1 addition & 0 deletions src/studio-home/data/selectors.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const getStudioHomeData = state => state.studioHome.studioHomeData;
export const getLoadingStatuses = (state) => state.studioHome.loadingStatuses;
export const getSavingStatuses = (state) => state.studioHome.savingStatuses;
export const getStudioHomeCoursesParams = (state) => state.studioHome.studioHomeCoursesCustomParams;
12 changes: 11 additions & 1 deletion src/studio-home/data/slice.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ const slice = createSlice({
deleteNotificationSavingStatus: '',
},
studioHomeData: {},
studioHomeCoursesCustomParams: {
currentPage: 1,
},
},
reducers: {
updateLoadingStatuses: (state, { payload }) => {
Expand All @@ -29,15 +32,21 @@ const slice = createSlice({
Object.assign(state.studioHomeData, payload);
},
fetchCourseDataSuccess: (state, { payload }) => {
const { courses, archivedCourses, inProcessCourseActions } = payload;
const { courses, archivedCourses = [], inProcessCourseActions } = payload.results;
const { numPages, count } = payload;
state.studioHomeData.courses = courses;
state.studioHomeData.archivedCourses = archivedCourses;
state.studioHomeData.inProcessCourseActions = inProcessCourseActions;
state.studioHomeData.numPages = numPages;
state.studioHomeData.coursesCount = count;
},
fetchLibraryDataSuccess: (state, { payload }) => {
const { libraries } = payload;
state.studioHomeData.libraries = libraries;
},
updateStudioHomeCoursesCustomParams: (state, { payload }) => {
state.studioHomeCoursesCustomParams = { ...state.studioHomeCoursesCustomParams, ...payload };
Comment thread
johnvente marked this conversation as resolved.
Outdated
Comment thread
johnvente marked this conversation as resolved.
Outdated
},
},
});

Expand All @@ -47,6 +56,7 @@ export const {
fetchStudioHomeDataSuccess,
fetchCourseDataSuccess,
fetchLibraryDataSuccess,
updateStudioHomeCoursesCustomParams,
} = slice.actions;

export const {
Expand Down
4 changes: 2 additions & 2 deletions src/studio-home/data/thunks.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
fetchLibraryDataSuccess,
} from './slice';

function fetchStudioHomeData(search, hasHomeData) {
function fetchStudioHomeData(search, hasHomeData, customParams = {}) {
Comment thread
johnvente marked this conversation as resolved.
Outdated
return async (dispatch) => {
dispatch(updateLoadingStatuses({ studioHomeLoadingStatus: RequestStatus.IN_PROGRESS }));
dispatch(updateLoadingStatuses({ courseLoadingStatus: RequestStatus.IN_PROGRESS }));
Expand All @@ -30,7 +30,7 @@ function fetchStudioHomeData(search, hasHomeData) {
}
}
try {
const coursesData = await getStudioHomeCourses(search || '');
const coursesData = await getStudioHomeCourses(search || '', customParams);
Comment thread
johnvente marked this conversation as resolved.
Outdated
dispatch(fetchCourseDataSuccess(coursesData));
dispatch(updateLoadingStatuses({ courseLoadingStatus: RequestStatus.SUCCESSFUL }));
} catch (error) {
Expand Down
100 changes: 55 additions & 45 deletions src/studio-home/factories/mockApiResponses.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export const initialState = {
deleteNotificationSavingStatus: '',
},
studioHomeData: {},
studioHomeCoursesCustomParams: {
currentPage: 1,
},
},
};

Expand Down Expand Up @@ -46,51 +49,58 @@ export const generateGetStudioHomeDataApiResponse = () => ({
});

export const generateGetStudioCoursesApiResponse = () => ({
archivedCourses: [
{
courseKey: 'course-v1:MachineLearning+123+2023',
displayName: 'Machine Learning',
lmsLink: '//localhost:18000/courses/course-v1:MachineLearning+123+2023/jump_to/block-v1:MachineLearning+123+2023+type@course+block@course',
number: '123',
org: 'LSE',
rerunLink: '/course_rerun/course-v1:MachineLearning+123+2023',
run: '2023',
url: '/course/course-v1:MachineLearning+123+2023',
},
{
courseKey: 'course-v1:Design+123+e.g.2025',
displayName: 'Design',
lmsLink: '//localhost:18000/courses/course-v1:Design+123+e.g.2025/jump_to/block-v1:Design+123+e.g.2025+type@course+block@course',
number: '123',
org: 'University of Cape Town',
rerunLink: '/course_rerun/course-v1:Design+123+e.g.2025',
run: 'e.g.2025',
url: '/course/course-v1:Design+123+e.g.2025',
},
],
courses: [
{
courseKey: 'course-v1:HarvardX+123+2023',
displayName: 'Managing Risk in the Information Age',
lmsLink: '//localhost:18000/courses/course-v1:HarvardX+123+2023/jump_to/block-v1:HarvardX+123+2023+type@course+block@course',
number: '123',
org: 'HarvardX',
rerunLink: '/course_rerun/course-v1:HarvardX+123+2023',
run: '2023',
url: '/course/course-v1:HarvardX+123+2023',
},
{
courseKey: 'org.0/course_0/Run_0',
displayName: 'Run 0',
lmsLink: null,
number: 'course_0',
org: 'org.0',
rerunLink: null,
run: 'Run_0',
url: null,
},
],
inProcessCourseActions: [],
count: 5,
next: null,
previous: null,
numPages: 2,
results: {
archivedCourses: [
{
courseKey: 'course-v1:MachineLearning+123+2023',
displayName: 'Machine Learning',
lmsLink: '//localhost:18000/courses/course-v1:MachineLearning+123+2023/jump_to/block-v1:MachineLearning+123+2023+type@course+block@course',
number: '123',
org: 'LSE',
rerunLink: '/course_rerun/course-v1:MachineLearning+123+2023',
run: '2023',
url: '/course/course-v1:MachineLearning+123+2023',
},
{
courseKey: 'course-v1:Design+123+e.g.2025',
displayName: 'Design',
lmsLink: '//localhost:18000/courses/course-v1:Design+123+e.g.2025/jump_to/block-v1:Design+123+e.g.2025+type@course+block@course',
number: '123',
org: 'University of Cape Town',
rerunLink: '/course_rerun/course-v1:Design+123+e.g.2025',
run: 'e.g.2025',
url: '/course/course-v1:Design+123+e.g.2025',
},
],
Comment thread
johnvente marked this conversation as resolved.
Outdated
courses: [
{
courseKey: 'course-v1:HarvardX+123+2023',
displayName: 'Managing Risk in the Information Age',
lmsLink: '//localhost:18000/courses/course-v1:HarvardX+123+2023/jump_to/block-v1:HarvardX+123+2023+type@course+block@course',
number: '123',
org: 'HarvardX',
rerunLink: '/course_rerun/course-v1:HarvardX+123+2023',
run: '2023',
url: '/course/course-v1:HarvardX+123+2023',
},
{
courseKey: 'org.0/course_0/Run_0',
displayName: 'Run 0',
lmsLink: null,
number: 'course_0',
org: 'org.0',
rerunLink: null,
run: 'Run_0',
url: null,
},
],
inProcessCourseActions: [],
},

Comment thread
johnvente marked this conversation as resolved.
Outdated
});

export const generateGetStuioHomeLibrariesApiResponse = () => ({
Expand Down
7 changes: 7 additions & 0 deletions src/studio-home/hooks.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import {
getLoadingStatuses,
getSavingStatuses,
getStudioHomeData,
getStudioHomeCoursesParams,
} from './data/selectors';
import { updateSavingStatuses } from './data/slice';

const useStudioHome = () => {
const location = useLocation();
const dispatch = useDispatch();
const studioHomeData = useSelector(getStudioHomeData);
const studioHomeCoursesParams = useSelector(getStudioHomeCoursesParams);
const newCourseData = useSelector(getCourseData);
const { studioHomeLoadingStatus } = useSelector(getLoadingStatuses);
const savingCreateRerunStatus = useSelector(getSavingStatus);
Expand All @@ -33,6 +35,11 @@ const useStudioHome = () => {
setShowNewCourseContainer(false);
}, [location.search]);

useEffect(() => {
const { currentPage } = studioHomeCoursesParams;
dispatch(fetchStudioHomeData(location.search ?? '', false, { page: currentPage }));
}, [studioHomeCoursesParams.currentPage]);
Comment thread
jesperhodge marked this conversation as resolved.
Outdated

useEffect(() => {
if (courseCreatorSavingStatus === RequestStatus.SUCCESSFUL) {
dispatch(updateSavingStatuses({ courseCreatorSavingStatus: '' }));
Expand Down
4 changes: 4 additions & 0 deletions src/studio-home/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ const messages = defineMessages({
id: 'course-authoring.studio-home.btn.view-live.text',
defaultMessage: 'View live',
},
editStudioBtnText: {
id: 'course-authoring.studio-home.btn.edit.studio.text',
defaultMessage: 'Edit in Studio',
},
organizationTitle: {
id: 'course-authoring.studio-home.organization.title',
defaultMessage: 'Organization and library settings',
Expand Down
Loading