Skip to content

Commit

Permalink
Merge pull request #3 from open-craft/0x29a/bb8368/port-edx-enterpris…
Browse files Browse the repository at this point in the history
…e-code-drift

feat: cherry-pick frontend-app-learner-portal-enterprise customizations to Quince [BB-8368]
  • Loading branch information
0x29a authored Jan 3, 2024
2 parents 72bf40c + 7f27884 commit f3e9a9e
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 43 deletions.
15 changes: 3 additions & 12 deletions src/components/my-career/CategoryCard.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, {
useMemo, useState, useEffect, useContext,
useState, useEffect, useContext,
} from 'react';

import PropTypes from 'prop-types';
Expand All @@ -9,10 +9,10 @@ import {
useToggle,
} from '@edx/paragon';
import { getConfig } from '@edx/frontend-platform/config';
import algoliasearch from 'algoliasearch/lite';
import { AppContext } from '@edx/frontend-platform/react';
import LevelBars from './LevelBars';
import SkillsRecommendationCourses from './SkillsRecommendationCourses';
import { useAlgoliaSearch } from '../../utils/hooks';

const CategoryCard = ({ topCategory }) => {
const { skillsSubcategories } = topCategory;
Expand All @@ -26,16 +26,7 @@ const CategoryCard = ({ topCategory }) => {

const config = getConfig();
const { enterpriseConfig } = useContext(AppContext);
const courseIndex = useMemo(
() => {
const client = algoliasearch(
config.ALGOLIA_APP_ID,
config.ALGOLIA_SEARCH_API_KEY,
);
return client.initIndex(config.ALGOLIA_INDEX_NAME);
},
[config.ALGOLIA_APP_ID, config.ALGOLIA_INDEX_NAME, config.ALGOLIA_SEARCH_API_KEY],
);
const [, courseIndex] = useAlgoliaSearch(config, config.ALGOLIA_INDEX_NAME);

const filterRenderableSkills = (skills) => {
const renderableSkills = [];
Expand Down
16 changes: 3 additions & 13 deletions src/components/search/Search.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, {
useContext, useMemo, useEffect,
useContext, useEffect,
} from 'react';
import { useParams, useHistory } from 'react-router-dom';
import { Helmet } from 'react-helmet';
Expand All @@ -9,7 +9,6 @@ import { getConfig } from '@edx/frontend-platform/config';
import { SearchHeader, SearchContext } from '@edx/frontend-enterprise-catalog-search';
import { useToggle, Stack } from '@edx/paragon';

import algoliasearch from 'algoliasearch/lite';
import { useDefaultSearchFilters, useSearchCatalogs } from './data/hooks';
import {
NUM_RESULTS_PER_PAGE,
Expand All @@ -36,6 +35,7 @@ import SearchPathwayCard from '../pathway/SearchPathwayCard';
import { SubsidyRequestsContext } from '../enterprise-subsidy-requests';
import PathwayModal from '../pathway/PathwayModal';
import { useEnterpriseCuration } from './content-highlights/data';
import { useAlgoliaSearch } from '../../utils/hooks';

const Search = () => {
const { pathwayUUID } = useParams();
Expand Down Expand Up @@ -78,17 +78,7 @@ const Search = () => {
}, [openLearnerPathwayModal, pathwayUUID]);

const config = getConfig();
const courseIndex = useMemo(
() => {
const client = algoliasearch(
config.ALGOLIA_APP_ID,
config.ALGOLIA_SEARCH_API_KEY,
);
const cIndex = client.initIndex(config.ALGOLIA_INDEX_NAME);
return cIndex;
},
[config.ALGOLIA_APP_ID, config.ALGOLIA_INDEX_NAME, config.ALGOLIA_SEARCH_API_KEY],
);
const [, courseIndex] = useAlgoliaSearch(config, config.ALGOLIA_INDEX_NAME);

const PAGE_TITLE = `${HEADER_TITLE} - ${enterpriseConfig.name}`;
const shouldDisplayBalanceAlert = hasNoEnterpriseOffersBalance || hasLowEnterpriseOffersBalance;
Expand Down
21 changes: 6 additions & 15 deletions src/components/skills-quiz/SkillsQuizStepper.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/* eslint-disable object-curly-newline */
import React, { useEffect, useState, useContext, useMemo } from 'react';
import React, { useEffect, useState, useContext } from 'react';
import {
Button, Stepper, ModalDialog, Container, Form, Stack,
} from '@edx/paragon';
import algoliasearch from 'algoliasearch/lite';
import { Configure, InstantSearch } from 'react-instantsearch-dom';
import { getConfig } from '@edx/frontend-platform/config';
import { SearchContext } from '@edx/frontend-enterprise-catalog-search';
Expand Down Expand Up @@ -38,6 +37,7 @@ import {
import { SkillsContext } from './SkillsContextProvider';
import { SET_KEY_VALUE } from './data/constants';
import { checkValidGoalAndJobSelected } from '../utils/skills-quiz';
import { useAlgoliaSearch } from '../../utils/hooks';
import TopSkillsOverview from './TopSkillsOverview';
import SkillsQuizHeader from './SkillsQuizHeader';

Expand All @@ -48,18 +48,9 @@ import { fetchCourseEnrollments } from './data/service';
const SkillsQuizStepper = () => {
const config = getConfig();
const { userId } = getAuthenticatedUser();
const [searchClient, courseIndex, jobIndex] = useMemo(
() => {
const client = algoliasearch(
config.ALGOLIA_APP_ID,
config.ALGOLIA_SEARCH_API_KEY,
);
const cIndex = client.initIndex(config.ALGOLIA_INDEX_NAME);
const jIndex = client.initIndex(config.ALGOLIA_INDEX_NAME_JOBS);
return [client, cIndex, jIndex];
},
[config.ALGOLIA_APP_ID, config.ALGOLIA_INDEX_NAME, config.ALGOLIA_INDEX_NAME_JOBS, config.ALGOLIA_SEARCH_API_KEY],
);
const [, courseIndex] = useAlgoliaSearch(config, config.ALGOLIA_INDEX_NAME);
const [jobSearchClient, jobIndex] = useAlgoliaSearch(config, config.ALGOLIA_INDEX_NAME_JOBS);

const [currentStep, setCurrentStep] = useState(STEP1);
const [isStudentChecked, setIsStudentChecked] = useState(false);
const handleIsStudentCheckedChange = e => setIsStudentChecked(e.target.checked);
Expand Down Expand Up @@ -198,7 +189,7 @@ const SkillsQuizStepper = () => {
<div>
<InstantSearch
indexName={config.ALGOLIA_INDEX_NAME_JOBS}
searchClient={searchClient}
searchClient={jobSearchClient}
>
<Configure
facetingAfterDistinct
Expand Down
1 change: 1 addition & 0 deletions src/components/skills-quiz/tests/SearchFilters.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { SubsidyRequestsContext } from '../../enterprise-subsidy-requests';
jest.mock('@edx/frontend-platform/auth', () => ({
...jest.requireActual('@edx/frontend-platform/auth'),
getAuthenticatedUser: () => ({ username: 'myspace-tom' }),
getAuthenticatedHttpClient: jest.fn(),
}));

jest.mock('@edx/frontend-enterprise-utils', () => ({
Expand Down
2 changes: 2 additions & 0 deletions src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ initialize({
LICENSE_MANAGER_URL: process.env.LICENSE_MANAGER_URL || null,
ALGOLIA_APP_ID: process.env.ALGOLIA_APP_ID || null,
ALGOLIA_SEARCH_API_KEY: process.env.ALGOLIA_SEARCH_API_KEY || null,
ALGOLIA_SECURED_KEY_ENDPOINT: process.env.ALGOLIA_SECURED_KEY_ENDPOINT
|| `${process.env.LMS_BASE_URL}/enterprise/api/v1/enterprise-customer/algolia_key/`,
ALGOLIA_INDEX_NAME: process.env.ALGOLIA_INDEX_NAME || null,
ALGOLIA_INDEX_NAME_JOBS: process.env.ALGOLIA_INDEX_NAME_JOBS || null,
INTEGRATION_WARNING_DISMISSED_COOKIE_NAME: process.env.INTEGRATION_WARNING_DISMISSED_COOKIE_NAME || null,
Expand Down
17 changes: 17 additions & 0 deletions src/utils/common.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Cookies from 'universal-cookie';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { getConfig } from '@edx/frontend-platform/config';
import { logError } from '@edx/frontend-platform/logging';
import dayjs from './dayjs';

export const isCourseEnded = endDate => dayjs(endDate) < dayjs();
Expand Down Expand Up @@ -66,6 +67,22 @@ export const loginRefresh = async () => {
}
};

export const fetchAlgoliaSecuredApiKey = async () => {
const config = getConfig();
const httpClient = getAuthenticatedHttpClient();

try {
const response = await httpClient.get(config.ALGOLIA_SECURED_KEY_ENDPOINT);
if (response && response.data) {
return response.data.key;
}
throw new Error('Response does not contain data');
} catch (error) {
logError(error);
return null;
}
};

export const fixedEncodeURIComponent = (str) => encodeURIComponent(str).replace(/[!()*]/g, (c) => `%${ c.charCodeAt(0).toString(16)}`);

export const formatStringAsNumber = (str, radix = 10) => {
Expand Down
37 changes: 34 additions & 3 deletions src/utils/hooks.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import React, { useCallback, useMemo } from 'react';
import React, {
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import algoliasearch from 'algoliasearch';

import { fetchAlgoliaSecuredApiKey } from './common';

export const useRenderContactHelpText = (enterpriseConfig) => {
const renderContactHelpText = useCallback(
(LinkComponent = 'a') => {
Expand All @@ -22,17 +29,41 @@ export const useRenderContactHelpText = (enterpriseConfig) => {
return renderContactHelpText;
};

let cachedApiKey = null;

export const useAlgoliaSearchApiKey = (config) => {
// If the search API key is not provided in the config,
// fetch it from `ALGOLIA_SECURED_KEY_ENDPOINT`.

const [searchApiKey, setSearchApiKey] = useState(cachedApiKey || config.ALGOLIA_SEARCH_API_KEY);

useEffect(() => {
const fetchApiKey = async () => {
const key = await fetchAlgoliaSecuredApiKey();
cachedApiKey = key;
setSearchApiKey(key);
};

if (!searchApiKey) {
fetchApiKey();
}
}, [searchApiKey]);

return searchApiKey;
};

export const useAlgoliaSearch = (config, indexName) => {
const algoliaSearchApiKey = useAlgoliaSearchApiKey(config);
const [searchClient, searchIndex] = useMemo(
() => {
const client = algoliasearch(
config.ALGOLIA_APP_ID,
config.ALGOLIA_SEARCH_API_KEY,
algoliaSearchApiKey,
);
const index = client.initIndex(indexName || config.ALGOLIA_INDEX_NAME);
return [client, index];
},
[config.ALGOLIA_APP_ID, config.ALGOLIA_INDEX_NAME, config.ALGOLIA_SEARCH_API_KEY, indexName],
[config.ALGOLIA_APP_ID, config.ALGOLIA_INDEX_NAME, algoliaSearchApiKey, indexName],
);
return [searchClient, searchIndex];
};

0 comments on commit f3e9a9e

Please sign in to comment.