Skip to content

Commit

Permalink
feat: enabled one academy feature. (#1054)
Browse files Browse the repository at this point in the history
  • Loading branch information
saleem-latif authored Apr 26, 2024
1 parent c0e341a commit d70b1d6
Show file tree
Hide file tree
Showing 16 changed files with 240 additions and 30 deletions.
36 changes: 36 additions & 0 deletions src/components/academies/GoToAcademy.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Link } from 'react-router-dom';
import { Button } from '@openedx/paragon';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import React from 'react';
import useEnterpriseCustomer from '../app/data/hooks/useEnterpriseCustomer';
import { useAcademies } from '../app/data';

const GoToAcademy = () => {
const { data: academies } = useAcademies();
const { data: enterpriseCustomer } = useEnterpriseCustomer();

return (
<>
<p>
<FormattedMessage
id="enterprise.dashboard.tab.courses.go.to.academy.message"
defaultMessage="Getting started with edX is easy. Simply find a course from your Academy, request enrollment, and get started on your learning journey."
description="Default message shown to a learner on enterprise dashboard."
/>
</p>
<Button
as={Link}
to={`/${enterpriseCustomer.slug}/academies/${academies[0].uuid}`}
className="btn-brand-primary d-block d-md-inline-block"
>
<FormattedMessage
id="enterprise.dashboard.tab.courses.go.to.academy"
defaultMessage="Go to Academy"
description="Label to go to academy button on enterprise dashboard."
/>
</Button>
</>
);
};

export default GoToAcademy;
8 changes: 5 additions & 3 deletions src/components/app/data/hooks/useRecommendCoursesForMe.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useMatch } from 'react-router-dom';

import useContentHighlightsConfiguration from './useContentHighlightsConfiguration';
import useIsAssignmentsOnlyLearner from './useIsAssignmentsOnlyLearner';
import useEnterpriseCustomer from './useEnterpriseCustomer';

/**
* Keeps track of whether the enterprise banner should include the "Recommend courses for me" button.
Expand All @@ -12,9 +13,10 @@ export default function useRecommendCoursesForMe() {
const { data: contentHighlightsConfiguration } = useContentHighlightsConfiguration();
const canOnlyViewHighlightSets = !!contentHighlightsConfiguration?.canOnlyViewHighlightSets;
const isAssignmentsOnlyLearner = useIsAssignmentsOnlyLearner();
// If user is not on the search page route, or users are restricted to only viewing highlight sets,
// the "Recommend courses for me" button should not be shown.
if (!isSearchPage || canOnlyViewHighlightSets) {
const { data: enterpriseCustomer } = useEnterpriseCustomer();
// If user is not on the search page route, or users are restricted to only viewing highlight sets or
// if the enterprise customer has enabled one academy, the "Recommend courses for me" button should not be shown.
if (!isSearchPage || canOnlyViewHighlightSets || enterpriseCustomer.enableOneAcademy) {
return {
shouldRecommendCourses: false,
};
Expand Down
18 changes: 17 additions & 1 deletion src/components/app/data/hooks/useRecommendCoursesForMe.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { AppContext } from '@edx/frontend-platform/react';
import useRecommendCoursesForMe from './useRecommendCoursesForMe';
import useContentHighlightsConfiguration from './useContentHighlightsConfiguration';
import useIsAssignmentsOnlyLearner from './useIsAssignmentsOnlyLearner';
import { authenticatedUserFactory } from '../services/data/__factories__';
import useEnterpriseCustomer from './useEnterpriseCustomer';
import { authenticatedUserFactory, enterpriseCustomerFactory } from '../services/data/__factories__';

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
Expand All @@ -18,8 +19,10 @@ jest.mock('./useContentHighlightsConfiguration', () => jest.fn().mockReturnValue
},
}));
jest.mock('./useIsAssignmentsOnlyLearner', () => jest.fn().mockReturnValue(false));
jest.mock('./useEnterpriseCustomer', () => jest.fn());

const mockAuthenticatedUser = authenticatedUserFactory();
const mockEnterpriseCustomer = enterpriseCustomerFactory();

const wrapper = ({ children }) => (
<AppContext.Provider value={{ authenticatedUser: mockAuthenticatedUser }}>
Expand All @@ -30,6 +33,7 @@ const wrapper = ({ children }) => (
describe('useRecommendCoursesForMe', () => {
beforeEach(() => {
jest.clearAllMocks();
useEnterpriseCustomer.mockReturnValue({ data: mockEnterpriseCustomer });
});

it('should not show recommend course CTA by default', () => {
Expand All @@ -44,24 +48,35 @@ describe('useRecommendCoursesForMe', () => {
mockRouteMatch: null, // simulates non-search page route
canOnlyViewHighlightSets: false,
isAssignmentsOnlyLearner: false,
enableOneAcademy: false,
hasRecommendCourseCTA: false,
},
{
mockRouteMatch: { path: '/search' }, // simulates search page route
canOnlyViewHighlightSets: false,
isAssignmentsOnlyLearner: false,
enableOneAcademy: false,
hasRecommendCourseCTA: true,
},
{
mockRouteMatch: { path: '/search' }, // simulates search page route
canOnlyViewHighlightSets: true,
isAssignmentsOnlyLearner: false,
enableOneAcademy: false,
hasRecommendCourseCTA: false,
},
{
mockRouteMatch: { path: '/search' }, // simulates search page route
canOnlyViewHighlightSets: false,
isAssignmentsOnlyLearner: false,
enableOneAcademy: true,
hasRecommendCourseCTA: false,
},
])('should support showing recommend course CTA, when appropriate (%s)', async ({
mockRouteMatch,
canOnlyViewHighlightSets,
isAssignmentsOnlyLearner,
enableOneAcademy,
hasRecommendCourseCTA,
}) => {
useMatch.mockReturnValue(mockRouteMatch);
Expand All @@ -71,6 +86,7 @@ describe('useRecommendCoursesForMe', () => {
},
});
useIsAssignmentsOnlyLearner.mockReturnValue(isAssignmentsOnlyLearner);
useEnterpriseCustomer.mockReturnValue({ data: { ...mockEnterpriseCustomer, enableOneAcademy } });

const { result } = renderHook(() => useRecommendCoursesForMe(), { wrapper });

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Factory } from 'rosie'; // eslint-disable-line import/no-extraneous-dependencies
import { faker } from '@faker-js/faker'; // eslint-disable-line import/no-extraneous-dependencies
import { camelCaseObject } from '@edx/frontend-platform';
import { v4 as uuidv4 } from 'uuid';

Factory.define('academy')
.attr('uuid', uuidv4())
.attr('title', faker.lorem.words())
.attr('short_description', faker.lorem.sentence(50))
.attr('long_description', faker.lorem.sentence(20))
.attr('image', faker.image.urlPlaceholder())
.attr('tags', []);

export function academyFactory(overrides = {}) {
return camelCaseObject(Factory.build('academy', overrides));
}

export function academiesFactory(count = 1, overrides = {}) {
return Array.from({ length: count }, () => academyFactory(overrides));
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Factory.define('enterpriseCustomer')
.attr('enable_data_sharing_consent', true)
.attr('admin_users', [{ email: faker.internet.email() }])
.attr('disable_search', false)
.attr('enable_one_academy', false)
.attr('branding_configuration', {
logo: faker.image.urlPlaceholder(),
primary_color: faker.internet.color(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import './enterpriseCustomerUser.factory';

export * from './enterpriseCustomerUser.factory';
export * from './academies.factory';
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,24 @@ import { QueryClientProvider } from '@tanstack/react-query';
import DashboardMainContent from './DashboardMainContent';
import { queryClient, renderWithRouter } from '../../../utils/tests';
import { features } from '../../../config';
import { useCanOnlyViewHighlights, useEnterpriseCourseEnrollments, useEnterpriseCustomer } from '../../app/data';
import { authenticatedUserFactory, enterpriseCustomerFactory } from '../../app/data/services/data/__factories__';
import {
useCanOnlyViewHighlights,
useEnterpriseCourseEnrollments,
useEnterpriseCustomer,
useAcademies,
} from '../../app/data';
import {
authenticatedUserFactory,
enterpriseCustomerFactory,
academiesFactory,
} from '../../app/data/services/data/__factories__';

jest.mock('../../app/data', () => ({
...jest.requireActual('../../app/data'),
useEnterpriseCustomer: jest.fn(),
useCanOnlyViewHighlights: jest.fn(),
useEnterpriseCourseEnrollments: jest.fn(),
useAcademies: jest.fn(),
}));

jest.mock('../../../config', () => ({
Expand All @@ -41,6 +51,7 @@ describe('DashboardMainContent', () => {
beforeEach(() => {
jest.clearAllMocks();
useEnterpriseCustomer.mockReturnValue({ data: mockEnterpriseCustomer });
useAcademies.mockReturnValue({ data: academiesFactory(3) });
useCanOnlyViewHighlights.mockReturnValue({ data: false });
useEnterpriseCourseEnrollments.mockReturnValue({
data: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import { Link } from 'react-router-dom';

import {
useEnterpriseCustomer,
useCanOnlyViewHighlights,
useCanOnlyViewHighlights, useAcademies,
} from '../../../app/data';
import CourseRecommendations from '../CourseRecommendations';
import GoToAcademy from '../../../academies/GoToAcademy';

const CourseEnrollmentsEmptyState = () => {
const { data: enterpriseCustomer } = useEnterpriseCustomer();
const { data: canOnlyViewHighlightSets } = useCanOnlyViewHighlights();
const { data: academies } = useAcademies();

if (enterpriseCustomer.disableSearch) {
return (
<p>
Expand All @@ -25,6 +28,11 @@ const CourseEnrollmentsEmptyState = () => {
</p>
);
}

if (enterpriseCustomer.enableOneAcademy && academies?.length === 1) {
return <GoToAcademy />;
}

return (
<>
<p>
Expand All @@ -45,7 +53,6 @@ const CourseEnrollmentsEmptyState = () => {
description="Label for Find a course button on enterprise dashboard's courses tab."
/>
</Button>

<br />
{canOnlyViewHighlightSets === false && <CourseRecommendations />}
</>
Expand Down
9 changes: 8 additions & 1 deletion src/components/dashboard/tests/DashboardPage.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
useCouponCodes,
useEnterpriseCourseEnrollments,
useEnterpriseCustomer,
useAcademies,
useEnterpriseOffers,
useEnterprisePathwaysList,
useEnterpriseProgramsList,
Expand All @@ -33,7 +34,11 @@ import {
useSubscriptions,
useHasAvailableSubsidiesOrRequests,
} from '../../app/data';
import { authenticatedUserFactory, enterpriseCustomerFactory } from '../../app/data/services/data/__factories__';
import {
authenticatedUserFactory,
enterpriseCustomerFactory,
academiesFactory,
} from '../../app/data/services/data/__factories__';

const dummyProgramData = {
uuid: 'test-uuid',
Expand Down Expand Up @@ -98,6 +103,7 @@ jest.mock('../../app/data', () => ({
useBrowseAndRequest: jest.fn(),
useIsAssignmentsOnlyLearner: jest.fn(),
useHasAvailableSubsidiesOrRequests: jest.fn(),
useAcademies: jest.fn(),
}));

jest.mock('@edx/frontend-enterprise-utils', () => ({
Expand Down Expand Up @@ -200,6 +206,7 @@ describe('<Dashboard />', () => {
beforeEach(() => {
jest.clearAllMocks();
useEnterpriseCustomer.mockReturnValue({ data: mockEnterpriseCustomer });
useAcademies.mockReturnValue({ data: academiesFactory(3) });
useSubscriptions.mockReturnValue({
data: {
subscriptionLicense: undefined,
Expand Down
21 changes: 15 additions & 6 deletions src/components/search/data/searchLoader.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { generatePath, redirect } from 'react-router-dom';
import { getConfig } from '@edx/frontend-platform/config';
import { ensureAuthenticatedUser } from '../../app/routes/data';
import { ensureAuthenticatedUser } from '../../app/routes/data/utils';
import { extractEnterpriseCustomer, queryAcademiesList, queryContentHighlightSets } from '../../app/data';

export default function makeSearchLoader(queryClient) {
Expand All @@ -18,11 +19,10 @@ export default function makeSearchLoader(queryClient) {
authenticatedUser,
enterpriseSlug,
});
const searchData = [
queryClient.ensureQueryData(
queryAcademiesList(enterpriseCustomer.uuid),
),
];

const academiesListQuery = queryAcademiesList(enterpriseCustomer.uuid);

const searchData = [queryClient.ensureQueryData(academiesListQuery)];
if (getConfig().FEATURE_CONTENT_HIGHLIGHTS) {
searchData.push(
queryClient.ensureQueryData(
Expand All @@ -33,6 +33,15 @@ export default function makeSearchLoader(queryClient) {

await Promise.all(searchData);

const academies = queryClient.getQueryData(academiesListQuery.queryKey);
if (enterpriseCustomer.enableOneAcademy && academies.length === 1) {
const redirectPath = generatePath('/:enterpriseSlug/academies/:academyUUID', {
enterpriseSlug,
academyUUID: academies[0].uuid,
});
return redirect(redirectPath);
}

return null;
};
}
Loading

0 comments on commit d70b1d6

Please sign in to comment.