@edx/frontend-app-subscription-learner-dashboard 1.53.2 → 1.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/containers/Dashboard/index.js +2 -3
- package/dist/containers/Dashboard/index.js.map +1 -1
- package/dist/containers/RecommendationsRailsPanel/Recommendations/index.d.ts +4 -0
- package/dist/containers/RecommendationsRailsPanel/Recommendations/index.js +7 -0
- package/dist/containers/RecommendationsRailsPanel/Recommendations/index.js.map +1 -0
- package/dist/containers/RecommendationsRailsPanel/RecommendationsRow.d.ts +5 -0
- package/dist/containers/RecommendationsRailsPanel/RecommendationsRow.js +24 -0
- package/dist/containers/RecommendationsRailsPanel/RecommendationsRow.js.map +1 -0
- package/dist/containers/RecommendationsRailsPanel/index.d.ts +2 -0
- package/dist/containers/RecommendationsRailsPanel/index.js +12 -0
- package/dist/containers/RecommendationsRailsPanel/index.js.map +1 -0
- package/dist/containers/RecommendationsRailsPanel/index.scss +7 -0
- package/dist/containers/RecommendationsRailsPanel/messages.d.ts +23 -0
- package/dist/containers/RecommendationsRailsPanel/messages.js +25 -0
- package/dist/containers/RecommendationsRailsPanel/messages.js.map +1 -0
- package/dist/hooks/usePopularCoursesAndPrograms.d.ts +3 -3
- package/dist/hooks/usePopularCoursesAndPrograms.js +4 -4
- package/dist/hooks/usePopularCoursesAndPrograms.js.map +1 -1
- package/dist/types/types.d.ts +1 -0
- package/dist/types/types.js.map +1 -1
- package/dist/utils/algoliaUtils.d.ts +3 -3
- package/dist/utils/algoliaUtils.js +6 -7
- package/dist/utils/algoliaUtils.js.map +1 -1
- package/dist/widgets/ProgressiveProfilingQuiz/DataRailSelection/index.d.ts +3 -0
- package/dist/widgets/ProgressiveProfilingQuiz/DataRailSelection/index.js +21 -0
- package/dist/widgets/ProgressiveProfilingQuiz/DataRailSelection/index.js.map +1 -0
- package/dist/widgets/ProgressiveProfilingQuiz/app.js +8 -0
- package/dist/widgets/ProgressiveProfilingQuiz/app.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,8 +6,6 @@ import { useInitializeSubsCourseDashboard } from '../../data/hooks';
|
|
|
6
6
|
import SelectSessionModal from '../../containers/SelectSessionModal';
|
|
7
7
|
import { DashboardContent } from './DashboardContent';
|
|
8
8
|
import DashboardModalSlot from '../../slots/DashboardModalSlot';
|
|
9
|
-
import PopularCoursePanel from '../PopularCoursePanel';
|
|
10
|
-
import PopularProgramsPanel from '../PopularProgramsPanel';
|
|
11
9
|
import DashboardLayout from './DashboardLayout';
|
|
12
10
|
import hooks from './hooks';
|
|
13
11
|
import './index.scss';
|
|
@@ -15,6 +13,7 @@ import { SubscriptionBanner } from '../SubscriptionBanner';
|
|
|
15
13
|
import { useInitializeSubsDashboard } from '../../data/hooks';
|
|
16
14
|
import DashboardHeroSlot from '../../slots/DashboardHeroSlot';
|
|
17
15
|
import { useAnswers } from '../../widgets/ProgressiveProfilingQuiz/data/queryHooks';
|
|
16
|
+
import DataRailSelection from '../../widgets/ProgressiveProfilingQuiz/DataRailSelection';
|
|
18
17
|
export const Dashboard = () => {
|
|
19
18
|
const { data, isPending } = useInitializeSubsCourseDashboard();
|
|
20
19
|
const { data: ProgressiveProfileStatus } = useAnswers();
|
|
@@ -25,7 +24,7 @@ export const Dashboard = () => {
|
|
|
25
24
|
const learnerSubscriptionData = (learnerData === null || learnerData === void 0 ? void 0 : learnerData.userSubscription) || {};
|
|
26
25
|
const userSubscriptionStartDate = (learnerSubscriptionData === null || learnerSubscriptionData === void 0 ? void 0 : learnerSubscriptionData.subscriptionStartDate) || '';
|
|
27
26
|
const hasCourses = useMemo(() => { var _a; return ((_a = data === null || data === void 0 ? void 0 : data.subscriptionCourses) === null || _a === void 0 ? void 0 : _a.length) > 0; }, [data]);
|
|
28
|
-
return (_jsx("div", { id: "learnerdashboardroot", children: _jsx("main", { children: _jsxs("div", { id: "dashboard-container", className: "d-flex flex-column p-2 pt-0", children: [_jsx("h1", { className: "sr-only", children: pageTitle }), !isPending && (_jsxs(_Fragment, { children: [_jsx(DashboardModalSlot, {}), (hasCourses && showSelectSessionModal) && _jsx(SelectSessionModal, {})] })), _jsx("div", { id: "dashboard-content", "data-testid": "dashboard-content", children: _jsxs(DashboardLayout, { children: [_jsx(DashboardHeader, { startDate: userSubscriptionStartDate }), _jsx(SubscriptionBanner, { learnerSubscriptionData: learnerSubscriptionData }), _jsx(DashboardHeroSlot, {}), _jsx(DashboardContent, { progressiveProfileStatus: ProgressiveProfileStatus === null || ProgressiveProfileStatus === void 0 ? void 0 : ProgressiveProfileStatus.progressiveProfileStatus }), _jsx(
|
|
27
|
+
return (_jsx("div", { id: "learnerdashboardroot", children: _jsx("main", { children: _jsxs("div", { id: "dashboard-container", className: "d-flex flex-column p-2 pt-0", children: [_jsx("h1", { className: "sr-only", children: pageTitle }), !isPending && (_jsxs(_Fragment, { children: [_jsx(DashboardModalSlot, {}), (hasCourses && showSelectSessionModal) && _jsx(SelectSessionModal, {})] })), _jsx("div", { id: "dashboard-content", "data-testid": "dashboard-content", children: _jsxs(DashboardLayout, { children: [_jsx(DashboardHeader, { startDate: userSubscriptionStartDate }), _jsx(SubscriptionBanner, { learnerSubscriptionData: learnerSubscriptionData }), _jsx(DashboardHeroSlot, {}), _jsx(DashboardContent, { progressiveProfileStatus: ProgressiveProfileStatus === null || ProgressiveProfileStatus === void 0 ? void 0 : ProgressiveProfileStatus.progressiveProfileStatus }), _jsx(DataRailSelection, {})] }) })] }) }) }));
|
|
29
28
|
};
|
|
30
29
|
export default Dashboard;
|
|
31
30
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/containers/Dashboard/index.jsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,gCAAgC,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,kBAAkB,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,kBAAkB,MAAM,gCAAgC,CAAC;AAChE,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/containers/Dashboard/index.jsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,gCAAgC,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,kBAAkB,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,kBAAkB,MAAM,gCAAgC,CAAC;AAChE,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,OAAO,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,iBAAiB,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,uDAAuD,CAAC;AACnF,OAAO,iBAAiB,MAAM,yDAAyD,CAAC;AAGxF,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,EAAE;IAC5B,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,gCAAgC,EAAE,CAAC;IAC/D,MAAM,EAAE,IAAI,EAAE,wBAAwB,EAAE,GAAG,UAAU,EAAE,CAAC;IACxD,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,0BAA0B,EAAE,CAAC;IAC3D,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC,oBAAoB,EAAE,CAAC;IACnD,MAAM,EAAE,kBAAkB,EAAE,GAAG,qBAAqB,EAAE,CAAC;IACvD,MAAM,sBAAsB,GAAG,kBAAkB,CAAC,MAAM,KAAK,IAAI,CAAC;IAClE,MAAM,uBAAuB,GAAG,CAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,gBAAgB,KAAI,EAAE,CAAC;IACpE,MAAM,yBAAyB,GAAG,CAAA,uBAAuB,aAAvB,uBAAuB,uBAAvB,uBAAuB,CAAE,qBAAqB,KAAI,EAAE,CAAC;IACvF,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,WAAC,OAAA,CAAA,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,mBAAmB,0CAAE,MAAM,IAAG,CAAC,CAAA,EAAA,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEhF,OAAO,CACL,cAAK,EAAE,EAAC,sBAAsB,YAC5B,yBACE,eAAK,EAAE,EAAC,qBAAqB,EAAC,SAAS,EAAC,6BAA6B,aACnE,aAAI,SAAS,EAAC,SAAS,YAAE,SAAS,GAAM,EACvC,CAAC,SAAS,IAAI,CACb,8BACE,KAAC,kBAAkB,KAAG,EACrB,CAAC,UAAU,IAAI,sBAAsB,CAAC,IAAI,KAAC,kBAAkB,KAAG,IAChE,CACJ,EACD,cAAK,EAAE,EAAC,mBAAmB,iBAAa,mBAAmB,YACzD,MAAC,eAAe,eACd,KAAC,eAAe,IAAC,SAAS,EAAE,yBAAyB,GAAI,EACzD,KAAC,kBAAkB,IAAC,uBAAuB,EAAE,uBAAuB,GAAI,EACxE,KAAC,iBAAiB,KAAG,EACrB,KAAC,gBAAgB,IAAC,wBAAwB,EAAE,wBAAwB,aAAxB,wBAAwB,uBAAxB,wBAAwB,CAAE,wBAAwB,GAAG,EACjG,KAAC,iBAAiB,KAAG,IACL,GACd,IACF,GACD,GACH,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,SAAS,CAAC","sourcesContent":["import { useMemo } from 'react';\nimport { DashboardHeader } from './DashboardHeader';\n\nimport { useSelectSessionModal } from '@src/data/context';\nimport { useInitializeSubsCourseDashboard } from '@src/data/hooks';\nimport SelectSessionModal from '../../containers/SelectSessionModal';\nimport { DashboardContent } from './DashboardContent';\nimport DashboardModalSlot from '../../slots/DashboardModalSlot';\nimport DashboardLayout from './DashboardLayout';\nimport hooks from './hooks';\nimport './index.scss';\nimport { SubscriptionBanner } from '../SubscriptionBanner';\nimport { useInitializeSubsDashboard } from '@src/data/hooks';\nimport DashboardHeroSlot from '@src/slots/DashboardHeroSlot';\nimport { useAnswers } from '@src/widgets/ProgressiveProfilingQuiz/data/queryHooks';\nimport DataRailSelection from '@src/widgets/ProgressiveProfilingQuiz/DataRailSelection';\n\n\nexport const Dashboard = () => {\n const { data, isPending } = useInitializeSubsCourseDashboard();\n const { data: ProgressiveProfileStatus } = useAnswers();\n const { data: learnerData } = useInitializeSubsDashboard();\n const { pageTitle } = hooks.useDashboardMessages();\n const { selectSessionModal } = useSelectSessionModal();\n const showSelectSessionModal = selectSessionModal.cardId !== null;\n const learnerSubscriptionData = learnerData?.userSubscription || {};\n const userSubscriptionStartDate = learnerSubscriptionData?.subscriptionStartDate || '';\n const hasCourses = useMemo(() => data?.subscriptionCourses?.length > 0, [data]);\n\n return (\n <div id=\"learnerdashboardroot\">\n <main>\n <div id=\"dashboard-container\" className=\"d-flex flex-column p-2 pt-0\">\n <h1 className=\"sr-only\">{pageTitle}</h1>\n {!isPending && (\n <>\n <DashboardModalSlot />\n {(hasCourses && showSelectSessionModal) && <SelectSessionModal />}\n </>\n )}\n <div id=\"dashboard-content\" data-testid=\"dashboard-content\">\n <DashboardLayout>\n <DashboardHeader startDate={userSubscriptionStartDate} />\n <SubscriptionBanner learnerSubscriptionData={learnerSubscriptionData} />\n <DashboardHeroSlot />\n <DashboardContent progressiveProfileStatus={ProgressiveProfileStatus?.progressiveProfileStatus}/>\n <DataRailSelection />\n </DashboardLayout>\n </div>\n </div>\n </main>\n </div>\n );\n};\n\nexport default Dashboard;\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { ProductCard } from '../../ProductCard';
|
|
3
|
+
export const Recommendations = ({ items }) => {
|
|
4
|
+
return (_jsx("div", { "data-testid": "popular-courses-items", children: _jsx("div", { className: "row g-3 flex-nowrap flex-md-wrap overflow-auto", children: items.map((item) => (_jsx("div", { className: "col-12 col-md-6 col-xl-3 d-flex mb-2", children: _jsx(ProductCard, { item: item }) }, item.objectID))) }) }));
|
|
5
|
+
};
|
|
6
|
+
export default Recommendations;
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/containers/RecommendationsRailsPanel/Recommendations/index.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,MAAM,CAAC,MAAM,eAAe,GAA6B,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;IACrE,OAAO,CACL,6BAAiB,uBAAuB,YACtC,cAAK,SAAS,EAAC,gDAAgD,YAC5D,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACnB,cAAyB,SAAS,EAAC,sCAAsC,YACvE,KAAC,WAAW,IAAC,IAAI,EAAE,IAAI,GAAI,IADnB,IAAI,CAAC,QAAQ,CAEjB,CACP,CAAC,GACE,GACF,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,eAAe,CAAC","sourcesContent":["import { FC } from 'react';\n\nimport { ProductCard } from '../../ProductCard';\nimport { RecommendationsProps } from './data/types';\n\nexport const Recommendations: FC<RecommendationsProps> = ({ items }) => {\n return (\n <div data-testid=\"popular-courses-items\">\n <div className=\"row g-3 flex-nowrap flex-md-wrap overflow-auto\">\n {items.map((item) => (\n <div key={item.objectID} className=\"col-12 col-md-6 col-xl-3 d-flex mb-2\">\n <ProductCard item={item} />\n </div>\n ))}\n </div>\n </div>\n );\n};\n\nexport default Recommendations;\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { useIntl } from '@openedx/frontend-base';
|
|
4
|
+
import { usePopularCoursesAndProgramsData } from '../../hooks/usePopularCoursesAndPrograms';
|
|
5
|
+
import { useAlgoliaSearch } from '../../utils/algoliaUtils';
|
|
6
|
+
import { PRODUCT_TYPE_COURSE } from '../../constants';
|
|
7
|
+
import { Alert } from '@openedx/paragon';
|
|
8
|
+
import messages from './messages';
|
|
9
|
+
import './index.scss';
|
|
10
|
+
import { Recommendations } from './Recommendations';
|
|
11
|
+
export const RecommendationsRow = ({ skill }) => {
|
|
12
|
+
const { formatMessage } = useIntl();
|
|
13
|
+
const [searchClient] = useAlgoliaSearch();
|
|
14
|
+
const { data: recommendationsData = [], isError, isLoading, } = usePopularCoursesAndProgramsData(searchClient, true, PRODUCT_TYPE_COURSE, skill);
|
|
15
|
+
const hasrecommendationsData = useMemo(() => recommendationsData.length > 0, [recommendationsData]);
|
|
16
|
+
if (isError) {
|
|
17
|
+
return (_jsx("div", { className: "recommendation-courses-container mt-5 mb-3", "data-testid": "recommendation-courses-panel", children: _jsx(Alert, { variant: "danger", "data-testid": "recommendation-courses-error", children: formatMessage(messages.recommendationsErrorText) }) }));
|
|
18
|
+
}
|
|
19
|
+
if (isLoading) {
|
|
20
|
+
return (_jsx("p", { "data-testid": "recommendation-courses-loading", role: "status", "aria-live": "polite", children: formatMessage(messages.recommendationsLoadingText) }));
|
|
21
|
+
}
|
|
22
|
+
return (_jsx("div", { className: "recommendation-courses-container mt-3 mb-3", "data-testid": "recommendation-courses-panel", children: hasrecommendationsData && (_jsxs(_Fragment, { children: [_jsxs("h3", { className: "recommendations-header", children: [formatMessage(messages.recommendationsHeader), " ", _jsx("span", { className: "recommendations-skill", children: skill })] }), _jsx(Recommendations, { items: recommendationsData })] })) }));
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=RecommendationsRow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RecommendationsRow.js","sourceRoot":"","sources":["../../../src/containers/RecommendationsRailsPanel/RecommendationsRow.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAM,OAAO,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gCAAgC,EAAE,MAAM,yCAAyC,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,QAAQ,MAAM,YAAY,CAAC;AAClC,OAAO,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,CAAC,MAAM,kBAAkB,GAA0B,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;IACrE,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IACpC,MAAM,CAAC,YAAY,CAAC,GAAG,gBAAgB,EAAE,CAAC;IAE1C,MAAM,EACJ,IAAI,EAAE,mBAAmB,GAAG,EAAE,EAC9B,OAAO,EACP,SAAS,GACV,GAAG,gCAAgC,CAAC,YAAY,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,CAAC,CAAC;IAErF,MAAM,sBAAsB,GAAG,OAAO,CACpC,GAAG,EAAE,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,EACpC,CAAC,mBAAmB,CAAC,CACtB,CAAC;IAEF,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CACL,cAAK,SAAS,EAAC,4CAA4C,iBAAa,8BAA8B,YACpG,KAAC,KAAK,IAAC,OAAO,EAAC,QAAQ,iBAAa,8BAA8B,YAC/D,aAAa,CAAC,QAAQ,CAAC,wBAAwB,CAAC,GAC3C,GACJ,CACP,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CACL,2BACc,gCAAgC,EAC5C,IAAI,EAAC,QAAQ,eACH,QAAQ,YAEjB,aAAa,CAAC,QAAQ,CAAC,0BAA0B,CAAC,GACjD,CACL,CAAC;IACJ,CAAC;IAED,OAAO,CACL,cAAK,SAAS,EAAC,4CAA4C,iBAAa,8BAA8B,YACnG,sBAAsB,IAAI,CACzB,8BACE,cAAI,SAAS,EAAC,wBAAwB,aAAE,aAAa,CAAC,QAAQ,CAAC,qBAAqB,CAAC,OAAE,eAAM,SAAS,EAAC,uBAAuB,YAAE,KAAK,GAAQ,IAAK,EAClJ,KAAC,eAAe,IAAC,KAAK,EAAE,mBAAmB,GAAI,IAC9C,CACJ,GACG,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import { FC, useMemo } from 'react';\nimport { useIntl } from '@openedx/frontend-base';\nimport { usePopularCoursesAndProgramsData } from '@src/hooks/usePopularCoursesAndPrograms';\nimport { useAlgoliaSearch } from '@src/utils/algoliaUtils';\nimport { PRODUCT_TYPE_COURSE } from '@src/constants';\nimport { Alert } from '@openedx/paragon';\nimport messages from './messages';\nimport './index.scss';\nimport { Recommendations } from './Recommendations';\n\nexport const RecommendationsRow: FC<{ skill: string }> = ({ skill }) => {\n const { formatMessage } = useIntl();\n const [searchClient] = useAlgoliaSearch();\n\n const {\n data: recommendationsData = [],\n isError,\n isLoading,\n } = usePopularCoursesAndProgramsData(searchClient, true, PRODUCT_TYPE_COURSE, skill);\n\n const hasrecommendationsData = useMemo(\n () => recommendationsData.length > 0,\n [recommendationsData]\n );\n\n if (isError) {\n return (\n <div className=\"recommendation-courses-container mt-5 mb-3\" data-testid=\"recommendation-courses-panel\">\n <Alert variant=\"danger\" data-testid=\"recommendation-courses-error\">\n {formatMessage(messages.recommendationsErrorText)}\n </Alert>\n </div>\n );\n }\n\n if (isLoading) {\n return (\n <p\n data-testid=\"recommendation-courses-loading\"\n role=\"status\"\n aria-live=\"polite\"\n >\n {formatMessage(messages.recommendationsLoadingText)}\n </p>\n );\n }\n\n return (\n <div className=\"recommendation-courses-container mt-3 mb-3\" data-testid=\"recommendation-courses-panel\">\n {hasrecommendationsData && (\n <>\n <h3 className=\"recommendations-header\">{formatMessage(messages.recommendationsHeader)} <span className=\"recommendations-skill\">{skill}</span></h3>\n <Recommendations items={recommendationsData} />\n </>\n )}\n </div>\n );\n};\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useProgressiveProfilingQuiz } from '../../widgets/ProgressiveProfilingQuiz/ProgressiveProfilingQuizProvider';
|
|
3
|
+
import { RecommendationsRow } from './RecommendationsRow';
|
|
4
|
+
export const RecommendationsRailsPanel = () => {
|
|
5
|
+
const { answers } = useProgressiveProfilingQuiz();
|
|
6
|
+
const skills = Object.values(answers)
|
|
7
|
+
.filter(answer => answer.questionId === 'interests-declared')
|
|
8
|
+
.map(answer => answer.selectedValues)
|
|
9
|
+
.flat();
|
|
10
|
+
return (_jsx("div", { className: "recommendation-courses-container mt-5 mb-3", "data-testid": "recommendation-courses-panel", children: (skills === null || skills === void 0 ? void 0 : skills.length) > 0 && (skills.slice(0, 4).map(skill => (_jsx("div", { children: _jsx(RecommendationsRow, { skill: skill }) }, skill)))) }));
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/containers/RecommendationsRailsPanel/index.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,2BAA2B,EAAE,MAAM,yEAAyE,CAAC;AACtH,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,CAAC,MAAM,yBAAyB,GAAO,GAAG,EAAE;IAChD,MAAM,EAAE,OAAO,EAAE,GAAG,2BAA2B,EAAE,CAAC;IAClD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;SAClC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,UAAU,KAAK,oBAAoB,CAAC;SAC5D,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC;SACpC,IAAI,EAAE,CAAC;IAEV,OAAO,CACL,cAAK,SAAS,EAAC,4CAA4C,iBAAa,8BAA8B,YACnG,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,MAAM,IAAG,CAAC,IAAI,CACrB,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAC9B,wBACE,KAAC,kBAAkB,IAAC,KAAK,EAAE,KAAK,GAAI,IAD5B,KAAK,CAET,CACP,CAAC,CACH,GACG,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import { FC } from 'react';\nimport { useProgressiveProfilingQuiz } from '../../widgets/ProgressiveProfilingQuiz/ProgressiveProfilingQuizProvider';\nimport { RecommendationsRow } from './RecommendationsRow';\n\nexport const RecommendationsRailsPanel: FC = () => {\n const { answers } = useProgressiveProfilingQuiz();\n const skills = Object.values(answers)\n .filter(answer => answer.questionId === 'interests-declared')\n .map(answer => answer.selectedValues)\n .flat();\n\n return (\n <div className=\"recommendation-courses-container mt-5 mb-3\" data-testid=\"recommendation-courses-panel\">\n {skills?.length > 0 && (\n skills.slice(0, 4).map(skill => (\n <div key={skill}>\n <RecommendationsRow skill={skill} />\n </div>\n ))\n )}\n </div>\n );\n};\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
declare const messages: {
|
|
2
|
+
recommendationsHeader: {
|
|
3
|
+
id: string;
|
|
4
|
+
defaultMessage: string;
|
|
5
|
+
description: string;
|
|
6
|
+
};
|
|
7
|
+
recommendationsErrorText: {
|
|
8
|
+
id: string;
|
|
9
|
+
defaultMessage: string;
|
|
10
|
+
description: string;
|
|
11
|
+
};
|
|
12
|
+
recommendationsLoadingText: {
|
|
13
|
+
id: string;
|
|
14
|
+
defaultMessage: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
recommendationsNoResultsText: {
|
|
18
|
+
id: string;
|
|
19
|
+
defaultMessage: string;
|
|
20
|
+
description: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export default messages;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { defineMessages } from '@openedx/frontend-base';
|
|
2
|
+
const messages = defineMessages({
|
|
3
|
+
recommendationsHeader: {
|
|
4
|
+
id: 'subs-dash.recommendationsPanel.header',
|
|
5
|
+
defaultMessage: 'Because of your interest in',
|
|
6
|
+
description: 'Label for the recommendation courses section',
|
|
7
|
+
},
|
|
8
|
+
recommendationsErrorText: {
|
|
9
|
+
id: 'subs-dash.recommendationsPanel.errorText',
|
|
10
|
+
defaultMessage: 'Failed to load recommendation courses. Please try again later.',
|
|
11
|
+
description: 'Error text for failing to load recommendation courses',
|
|
12
|
+
},
|
|
13
|
+
recommendationsLoadingText: {
|
|
14
|
+
id: 'subs-dash.recommendationsPanel.loadingText',
|
|
15
|
+
defaultMessage: 'Loading course recommendations.',
|
|
16
|
+
description: 'Loading text shown while recommendation courses data is being fetched',
|
|
17
|
+
},
|
|
18
|
+
recommendationsNoResultsText: {
|
|
19
|
+
id: 'subs-dash.recommendationsPanel.noResultsText',
|
|
20
|
+
defaultMessage: 'No course recommendations found.',
|
|
21
|
+
description: 'Text shown when no recommendation courses are found',
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
export default messages;
|
|
25
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../src/containers/RecommendationsRailsPanel/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,qBAAqB,EAAE;QACrB,EAAE,EAAE,uCAAuC;QAC3C,cAAc,EAAE,6BAA6B;QAC7C,WAAW,EAAE,8CAA8C;KAC5D;IACD,wBAAwB,EAAE;QACxB,EAAE,EAAE,0CAA0C;QAC9C,cAAc,EAAE,gEAAgE;QAChF,WAAW,EAAE,uDAAuD;KACrE;IACD,0BAA0B,EAAE;QAC1B,EAAE,EAAE,4CAA4C;QAChD,cAAc,EAAE,iCAAiC;QACjD,WAAW,EAAE,uEAAuE;KACrF;IACD,4BAA4B,EAAE;QAC5B,EAAE,EAAE,8CAA8C;QAClD,cAAc,EAAE,kCAAkC;QAClD,WAAW,EAAE,qDAAqD;KACnE;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n recommendationsHeader: {\n id: 'subs-dash.recommendationsPanel.header',\n defaultMessage: 'Because of your interest in',\n description: 'Label for the recommendation courses section',\n },\n recommendationsErrorText: {\n id: 'subs-dash.recommendationsPanel.errorText',\n defaultMessage: 'Failed to load recommendation courses. Please try again later.',\n description: 'Error text for failing to load recommendation courses',\n },\n recommendationsLoadingText: {\n id: 'subs-dash.recommendationsPanel.loadingText',\n defaultMessage: 'Loading course recommendations.',\n description: 'Loading text shown while recommendation courses data is being fetched',\n },\n recommendationsNoResultsText: {\n id: 'subs-dash.recommendationsPanel.noResultsText',\n defaultMessage: 'No course recommendations found.',\n description: 'Text shown when no recommendation courses are found',\n },\n});\n\nexport default messages;\n"]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { UseQueryResult } from '@tanstack/react-query';
|
|
2
2
|
import { SearchClient } from 'algoliasearch';
|
|
3
|
-
import { ProductType } from '../types/types';
|
|
3
|
+
import { ProductType, Skill } from '../types/types';
|
|
4
4
|
import { ProductCardItem } from '@src/containers/ProductCard/data/types';
|
|
5
5
|
export declare const searchKeys: {
|
|
6
6
|
all: readonly ["search"];
|
|
7
7
|
programs: (query: string, tags: string[]) => readonly ["search", "programs", string, string[]];
|
|
8
8
|
courses: (query: string, tags: string[], page: number) => readonly ["search", "courses", string, string[], number];
|
|
9
|
-
popular: (productType: ProductType, limit: number) => readonly ["search", "popular", ProductType, number];
|
|
9
|
+
popular: (productType: ProductType, limit: number, skill?: Skill) => readonly ["search", "popular", ProductType, number, string | null];
|
|
10
10
|
};
|
|
11
|
-
export declare const usePopularCoursesAndProgramsData: (searchClient: SearchClient | null, enabled: boolean, productType?: ProductType) => UseQueryResult<ProductCardItem[]>;
|
|
11
|
+
export declare const usePopularCoursesAndProgramsData: (searchClient: SearchClient | null, enabled: boolean, productType?: ProductType, skill?: Skill) => UseQueryResult<ProductCardItem[]>;
|
|
@@ -16,12 +16,12 @@ export const searchKeys = {
|
|
|
16
16
|
all: ['search'],
|
|
17
17
|
programs: (query, tags) => [...searchKeys.all, 'programs', query, tags],
|
|
18
18
|
courses: (query, tags, page) => [...searchKeys.all, 'courses', query, tags, page],
|
|
19
|
-
popular: (productType, limit) => [...searchKeys.all, 'popular', productType, limit],
|
|
19
|
+
popular: (productType, limit, skill) => [...searchKeys.all, 'popular', productType, limit, skill !== null && skill !== void 0 ? skill : null],
|
|
20
20
|
};
|
|
21
|
-
export const usePopularCoursesAndProgramsData = (searchClient, enabled, productType = PRODUCT_TYPE_COURSE) => useQuery({
|
|
22
|
-
queryKey: searchKeys.popular(productType, 4),
|
|
21
|
+
export const usePopularCoursesAndProgramsData = (searchClient, enabled, productType = PRODUCT_TYPE_COURSE, skill) => useQuery({
|
|
22
|
+
queryKey: searchKeys.popular(productType, 4, skill),
|
|
23
23
|
queryFn: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
24
|
-
const hits = yield fetchPopularProducts(searchClient, productType, 4);
|
|
24
|
+
const hits = yield fetchPopularProducts(searchClient, productType, 4, skill);
|
|
25
25
|
return hits.map((hit) => {
|
|
26
26
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
27
27
|
const hitDetails = hit;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePopularCoursesAndPrograms.js","sourceRoot":"","sources":["../../src/hooks/usePopularCoursesAndPrograms.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,QAAQ,EAAkB,MAAM,uBAAuB,CAAC;AAGjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAG/D,iFAAiF;AACjF,oDAAoD;AAEpD,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,GAAG,EAAE,CAAC,QAAQ,CAAU;IACxB,QAAQ,EAAE,CAAC,KAAa,EAAE,IAAc,EAAE,EAAE,CAC1C,CAAC,GAAG,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,CAAU;IACvD,OAAO,EAAE,CAAC,KAAa,EAAE,IAAc,EAAE,IAAY,EAAE,EAAE,CACvD,CAAC,GAAG,UAAU,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAU;IAC5D,OAAO,EAAE,CAAC,WAAwB,EAAE,KAAa,EAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"usePopularCoursesAndPrograms.js","sourceRoot":"","sources":["../../src/hooks/usePopularCoursesAndPrograms.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,QAAQ,EAAkB,MAAM,uBAAuB,CAAC;AAGjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAG/D,iFAAiF;AACjF,oDAAoD;AAEpD,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,GAAG,EAAE,CAAC,QAAQ,CAAU;IACxB,QAAQ,EAAE,CAAC,KAAa,EAAE,IAAc,EAAE,EAAE,CAC1C,CAAC,GAAG,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,CAAU;IACvD,OAAO,EAAE,CAAC,KAAa,EAAE,IAAc,EAAE,IAAY,EAAE,EAAE,CACvD,CAAC,GAAG,UAAU,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAU;IAC5D,OAAO,EAAE,CAAC,WAAwB,EAAE,KAAa,EAAE,KAAa,EAAE,EAAE,CAClE,CAAC,GAAG,UAAU,CAAC,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,IAAI,CAAU;CAC7E,CAAC;AAEF,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAC9C,YAAiC,EACjC,OAAgB,EAChB,cAA2B,mBAAmB,EAC9C,KAAa,EACsB,EAAE,CACrC,QAAQ,CAAC;IACP,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,EAAE,KAAK,CAAC;IACnD,OAAO,EAAE,GAAS,EAAE;QAClB,MAAM,IAAI,GAAG,MAAM,oBAAoB,CAAC,YAAa,EAAE,WAAW,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;QAC9E,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAmB,EAAE;;YACvC,MAAM,UAAU,GAAG,GAelB,CAAC;YAEF,OAAO;gBACL,QAAQ,EAAE,GAAG,CAAC,QAAQ;gBACtB,KAAK,EAAE,MAAA,MAAA,GAAG,CAAC,KAAK,mCAAI,GAAG,CAAC,IAAI,mCAAI,UAAU;gBAC1C,mBAAmB,EAAE,MAAA,MAAA,MAAA,UAAU,CAAC,mBAAmB,mCAAI,UAAU,CAAC,iBAAiB,mCAAI,UAAU,CAAC,WAAW,mCAAI,EAAE;gBACnH,GAAG,EAAE,MAAA,MAAA,UAAU,CAAC,cAAc,mCAAI,UAAU,CAAC,SAAS,mCAAI,EAAE;gBAC5D,aAAa,EAAE,MAAA,UAAU,CAAC,aAAa,mCAAI,EAAE;gBAC7C,SAAS,EAAE,MAAA,UAAU,CAAC,0BAA0B,mCAAI,EAAE;gBACtD,YAAY,EAAE,MAAA,MAAA,UAAU,CAAC,YAAY,mCAAI,UAAU,CAAC,OAAO,mCAAI,QAAQ;gBACvE,OAAO,EAAE,WAAW;gBACpB,OAAO,EAAE,MAAA,UAAU,CAAC,OAAO,mCAAI,EAAE;gBACjC,iBAAiB,EAAE,MAAA,UAAU,CAAC,iBAAiB,mCAAI,SAAS;gBAC5D,KAAK,EAAE,MAAA,UAAU,CAAC,KAAK,mCAAI,SAAS;gBACpC,OAAO,EAAE,MAAA,UAAU,CAAC,OAAO,mCAAI,SAAS;aACzC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAA;IACD,OAAO,EAAE,CAAC,CAAC,YAAY,IAAI,OAAO;IAClC,SAAS,EAAE,IAAI,GAAG,EAAE,GAAG,EAAE;CAC1B,CAAC,CAAC","sourcesContent":["import { useQuery, UseQueryResult } from '@tanstack/react-query';\nimport { SearchClient } from 'algoliasearch';\nimport { ProductType, Skill } from '@src/types/types';\nimport { PRODUCT_TYPE_COURSE } from '@src/constants';\nimport { fetchPopularProducts } from '@src/utils/algoliaUtils';\nimport { ProductCardItem } from '@src/containers/ProductCard/data/types';\n\n// ─── Query key factories ──────────────────────────────────────────────────────\n// Centralised so cache invalidation is never a typo\n\nexport const searchKeys = {\n all: ['search'] as const,\n programs: (query: string, tags: string[]) =>\n [...searchKeys.all, 'programs', query, tags] as const,\n courses: (query: string, tags: string[], page: number) =>\n [...searchKeys.all, 'courses', query, tags, page] as const,\n popular: (productType: ProductType, limit: number, skill?: Skill) =>\n [...searchKeys.all, 'popular', productType, limit, skill ?? null] as const,\n};\n\nexport const usePopularCoursesAndProgramsData = (\n searchClient: SearchClient | null,\n enabled: boolean,\n productType: ProductType = PRODUCT_TYPE_COURSE,\n skill?: Skill,\n): UseQueryResult<ProductCardItem[]> =>\n useQuery({\n queryKey: searchKeys.popular(productType, 4, skill),\n queryFn: async () => {\n const hits = await fetchPopularProducts(searchClient!, productType, 4, skill);\n return hits.map((hit): ProductCardItem => {\n const hitDetails = hit as {\n short_description?: string,\n primary_description?: string,\n description?: string,\n marketing_url?: string,\n image_url?: string,\n card_image_url?: string,\n organization_logo_override?: string,\n content_type?: string,\n program_type?: string,\n product?: string,\n partner?: string,\n weeks_to_complete?: number,\n level?: string,\n subject?: string,\n };\n\n return {\n objectID: hit.objectID,\n title: hit.title ?? hit.name ?? 'Untitled',\n primary_description: hitDetails.primary_description ?? hitDetails.short_description ?? hitDetails.description ?? '',\n url: hitDetails.card_image_url ?? hitDetails.image_url ?? '',\n marketing_url: hitDetails.marketing_url ?? '',\n thumbnail: hitDetails.organization_logo_override ?? '',\n content_type: hitDetails.program_type ?? hitDetails.product ?? 'Course',\n product: productType,\n partner: hitDetails.partner ?? '',\n weeks_to_complete: hitDetails.weeks_to_complete ?? undefined,\n level: hitDetails.level ?? undefined,\n subject: hitDetails.subject ?? undefined,\n };\n });\n },\n enabled: !!searchClient && enabled,\n staleTime: 1000 * 60 * 10,\n });\n"]}
|
package/dist/types/types.d.ts
CHANGED
package/dist/types/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG","sourcesContent":["/**\n * types/index.ts\n *\n * Shared TypeScript interfaces and types for the Search feature.\n */\n\n// ─── Algolia raw hit ──────────────────────────────────────────────────────────\n\n/** Raw shape returned by Algolia for a product hit. Only the fields we access. */\nexport interface AlgoliaProductHit {\n objectID: string,\n title?: string,\n name?: string,\n type?: string,\n content_type?: string,\n institution?: string,\n course_count?: number,\n courseCount?: number,\n card_image_url?: string,\n image_url?: string,\n authoring_organizations?: { name: string }[],\n partners?: { name: string }[],\n [key: string]: unknown, // allow arbitrary extra fields\n}\n\n/** Result shape returned by searchProducts / fetchPopularProducts */\nexport interface AlgoliaSearchResult {\n hits: AlgoliaProductHit[],\n nbHits: number,\n nbPages: number,\n}\n\n// ─── Product type union ───────────────────────────────────────────────────────\n\nexport type ProductType = 'program' | 'course';\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG","sourcesContent":["/**\n * types/index.ts\n *\n * Shared TypeScript interfaces and types for the Search feature.\n */\n\n// ─── Algolia raw hit ──────────────────────────────────────────────────────────\n\n/** Raw shape returned by Algolia for a product hit. Only the fields we access. */\nexport interface AlgoliaProductHit {\n objectID: string,\n title?: string,\n name?: string,\n type?: string,\n content_type?: string,\n institution?: string,\n course_count?: number,\n courseCount?: number,\n card_image_url?: string,\n image_url?: string,\n authoring_organizations?: { name: string }[],\n partners?: { name: string }[],\n [key: string]: unknown, // allow arbitrary extra fields\n}\n\n/** Result shape returned by searchProducts / fetchPopularProducts */\nexport interface AlgoliaSearchResult {\n hits: AlgoliaProductHit[],\n nbHits: number,\n nbPages: number,\n}\n\n// ─── Product type union ───────────────────────────────────────────────────────\n\nexport type ProductType = 'program' | 'course';\n\nexport type Skill = string; // e.g., 'Leadership', 'Communication', etc.\n"]}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Mirrors the pattern used by the Skills Builder feature, with full TypeScript types.
|
|
6
6
|
*/
|
|
7
7
|
import { SearchClient } from 'algoliasearch';
|
|
8
|
-
import { AlgoliaProductHit, AlgoliaSearchResult, ProductType } from '../types/types';
|
|
8
|
+
import { AlgoliaProductHit, AlgoliaSearchResult, ProductType, Skill } from '../types/types';
|
|
9
9
|
/**
|
|
10
10
|
* Creates and memoises an Algolia search client using app config.
|
|
11
11
|
* Matches the pattern used by the Skills Builder feature.
|
|
@@ -45,7 +45,7 @@ export declare const formatFacetFilterData: (facetFilterType: string, data: stri
|
|
|
45
45
|
* @param page 0-indexed page number (Algolia convention)
|
|
46
46
|
* @param staticFacetFilters Additional 'attribute:value' filters always AND-ed in, e.g. ['b2c_subscription_inclusion:true']
|
|
47
47
|
*/
|
|
48
|
-
export declare const searchProducts: (searchClient: SearchClient, productType: ProductType, query: string, activeTags: string[], facetField?: string, hitsPerPage?: number, page?: number, staticFacetFilters?: string[]) => Promise<AlgoliaSearchResult>;
|
|
48
|
+
export declare const searchProducts: (searchClient: SearchClient, productType: ProductType, query: string, activeTags: string[], facetField?: string, hitsPerPage?: number, page?: number, staticFacetFilters?: string[], skill?: Skill) => Promise<AlgoliaSearchResult>;
|
|
49
49
|
/**
|
|
50
50
|
* Fetches top-ranked products with no query or tag filters.
|
|
51
51
|
* Used for the "Popular programs / courses" fallback in the no-results state.
|
|
@@ -55,4 +55,4 @@ export declare const searchProducts: (searchClient: SearchClient, productType: P
|
|
|
55
55
|
* @param hitsPerPage Number of items to fetch
|
|
56
56
|
* @returns Raw Algolia hit objects
|
|
57
57
|
*/
|
|
58
|
-
export declare const fetchPopularProducts: (searchClient: SearchClient, productType: ProductType, hitsPerPage: number) => Promise<AlgoliaProductHit[]>;
|
|
58
|
+
export declare const fetchPopularProducts: (searchClient: SearchClient, productType: ProductType, hitsPerPage: number, skill?: Skill) => Promise<AlgoliaProductHit[]>;
|
|
@@ -79,24 +79,23 @@ export const formatFacetFilterData = (facetFilterType, data) => {
|
|
|
79
79
|
* @param page 0-indexed page number (Algolia convention)
|
|
80
80
|
* @param staticFacetFilters Additional 'attribute:value' filters always AND-ed in, e.g. ['b2c_subscription_inclusion:true']
|
|
81
81
|
*/
|
|
82
|
-
export const searchProducts = (searchClient_1, productType_1, query_1, activeTags_1, ...args_1) => __awaiter(void 0, [searchClient_1, productType_1, query_1, activeTags_1, ...args_1], void 0, function* (searchClient, productType, query, activeTags, facetField = FACET_SUBJECTS, hitsPerPage = HITS_PER_PAGE_COURSES, page = 0, staticFacetFilters = []) {
|
|
82
|
+
export const searchProducts = (searchClient_1, productType_1, query_1, activeTags_1, ...args_1) => __awaiter(void 0, [searchClient_1, productType_1, query_1, activeTags_1, ...args_1], void 0, function* (searchClient, productType, query, activeTags, facetField = FACET_SUBJECTS, hitsPerPage = HITS_PER_PAGE_COURSES, page = 0, staticFacetFilters = [], skill) {
|
|
83
83
|
const indexName = getAppConfig(appId).ALGOLIA_PRODUCT_INDEX_NAME;
|
|
84
84
|
const tagFilters = formatFacetFilterData(facetField, activeTags);
|
|
85
85
|
// Nested arrays = AND between outer elements, OR within inner arrays
|
|
86
86
|
const facetFilters = [
|
|
87
87
|
[`product:${productType}`],
|
|
88
|
+
...(skill ? [[`skills.skill:${skill}`]] : []),
|
|
88
89
|
...(tagFilters.length > 0 ? [tagFilters] : []),
|
|
89
90
|
...staticFacetFilters.map((filter) => [filter]),
|
|
90
91
|
];
|
|
91
92
|
try {
|
|
92
93
|
const { hits, nbHits, nbPages } = yield searchClient.searchSingleIndex({
|
|
93
94
|
indexName,
|
|
94
|
-
searchParams: {
|
|
95
|
-
query,
|
|
95
|
+
searchParams: Object.assign({ query,
|
|
96
96
|
facetFilters,
|
|
97
97
|
hitsPerPage,
|
|
98
|
-
page,
|
|
99
|
-
},
|
|
98
|
+
page }, (skill ? { facets: ['skills.skill'] } : {})),
|
|
100
99
|
});
|
|
101
100
|
return {
|
|
102
101
|
hits: hits,
|
|
@@ -120,10 +119,10 @@ export const searchProducts = (searchClient_1, productType_1, query_1, activeTag
|
|
|
120
119
|
* @param hitsPerPage Number of items to fetch
|
|
121
120
|
* @returns Raw Algolia hit objects
|
|
122
121
|
*/
|
|
123
|
-
export const fetchPopularProducts = (searchClient, productType, hitsPerPage) => __awaiter(void 0, void 0, void 0, function* () {
|
|
122
|
+
export const fetchPopularProducts = (searchClient, productType, hitsPerPage, skill) => __awaiter(void 0, void 0, void 0, function* () {
|
|
124
123
|
const { hits } = yield searchProducts(searchClient, productType, '', // empty query → Algolia ranks by popularity
|
|
125
124
|
[], // no tag filters
|
|
126
|
-
FACET_SUBJECTS, hitsPerPage, 0, ['b2c_subscription_inclusion:true']);
|
|
125
|
+
FACET_SUBJECTS, hitsPerPage, 0, ['b2c_subscription_inclusion:true'], skill);
|
|
127
126
|
return hits;
|
|
128
127
|
});
|
|
129
128
|
//# sourceMappingURL=algoliaUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"algoliaUtils.js","sourceRoot":"","sources":["../../src/utils/algoliaUtils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;;;;;;;;;;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAgB,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"algoliaUtils.js","sourceRoot":"","sources":["../../src/utils/algoliaUtils.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * utils/algoliaUtils.ts\n *\n * Algolia utility functions for the Search feature.\n * Mirrors the pattern used by the Skills Builder feature, with full TypeScript types.\n */\n\nimport { useMemo } from 'react';\nimport { getAppConfig, logError } from '@openedx/frontend-base';\nimport { algoliasearch, SearchClient } from 'algoliasearch';\nimport { FACET_SUBJECTS, HITS_PER_PAGE_COURSES } from '@src/constants';\n\nimport {\n AlgoliaProductHit,\n AlgoliaSearchResult,\n ProductType,\n Skill,\n} from '../types/types';\n\nimport { appId } from '@src/constants';\n\n// ─── useAlgoliaSearch ─────────────────────────────────────────────────────────\n\n/**\n * Creates and memoises an Algolia search client using app config.\n * Matches the pattern used by the Skills Builder feature.\n *\n * @returns [SearchClient | null] tuple — destructure as: const [searchClient] = useAlgoliaSearch()\n */\nexport const useAlgoliaSearch = (): [SearchClient | null] => {\n const config = getAppConfig(appId);\n const algoliaAppId = config.ALGOLIA_APP_ID as string | undefined;\n const algoliaSearchApiKey = config.ALGOLIA_SEARCH_API_KEY as string | undefined;\n\n const [searchClient] = useMemo<[SearchClient | null]>(\n () => {\n if (!algoliaAppId || !algoliaSearchApiKey) {\n logError(new Error('Missing Algolia configuration: ALGOLIA_APP_ID or ALGOLIA_SEARCH_API_KEY'));\n return [null];\n }\n\n const client = algoliasearch(algoliaAppId, algoliaSearchApiKey);\n return [client];\n },\n [algoliaAppId, algoliaSearchApiKey],\n );\n\n return [searchClient];\n};\n\n// ─── formatFacetFilterData ────────────────────────────────────────────────────\n\n/**\n * Formats an array of values into Algolia facet filter strings.\n *\n * @param facetFilterType Facet attribute name, e.g. 'subjects'\n * @param data Array of values, e.g. ['Leadership', 'AI']\n * @returns e.g. ['subjects:Leadership', 'subjects:AI']\n *\n * @example\n * formatFacetFilterData('subjects', ['Leadership', 'AI'])\n * // → ['subjects:Leadership', 'subjects:AI']\n */\nexport const formatFacetFilterData = (\n facetFilterType: string,\n data: string[],\n): string[] => {\n const formattedData: string[] = [];\n if (data) {\n data.forEach((item) => formattedData.push(`${facetFilterType}:${item}`));\n }\n return formattedData;\n};\n\n// ─── searchProducts ───────────────────────────────────────────────────────────\n\n/**\n * Searches programs or courses from the shared product index.\n *\n * Both product types live in ALGOLIA_PRODUCT_INDEX_NAME, distinguished by the\n * `product` facet. Active tags are OR-ed within their facet and AND-ed with\n * the product-type filter — standard Algolia multi-select pattern.\n *\n * Algolia facetFilters format used here:\n * [ ['product:program'], ['subjects:Leadership', 'subjects:AI'] ]\n * → product = \"program\" AND (subjects = \"Leadership\" OR subjects = \"AI\")\n *\n * @param searchClient Algolia client from useAlgoliaSearch()\n * @param productType 'program' | 'course'\n * @param query Free-text query ('' = browse / rank by popularity)\n * @param activeTags Selected tag values from the filter chips\n * @param facetField Facet attribute to filter tags on (default: 'subjects')\n * @param hitsPerPage Page size\n * @param page 0-indexed page number (Algolia convention)\n * @param staticFacetFilters Additional 'attribute:value' filters always AND-ed in, e.g. ['b2c_subscription_inclusion:true']\n */\nexport const searchProducts = async (\n searchClient: SearchClient,\n productType: ProductType,\n query: string,\n activeTags: string[],\n facetField: string = FACET_SUBJECTS,\n hitsPerPage: number = HITS_PER_PAGE_COURSES,\n page = 0,\n staticFacetFilters: string[] = [],\n skill?: Skill,\n): Promise<AlgoliaSearchResult> => {\n const indexName = getAppConfig(appId).ALGOLIA_PRODUCT_INDEX_NAME as string;\n const tagFilters = formatFacetFilterData(facetField, activeTags);\n\n // Nested arrays = AND between outer elements, OR within inner arrays\n const facetFilters: string[][] = [\n [`product:${productType}`],\n ...(skill ? [[`skills.skill:${skill}`]] : []),\n ...(tagFilters.length > 0 ? [tagFilters] : []),\n ...staticFacetFilters.map((filter) => [filter]),\n ];\n\n try {\n const { hits, nbHits, nbPages } = await searchClient.searchSingleIndex({\n indexName,\n searchParams: {\n query,\n facetFilters,\n hitsPerPage,\n page,\n ...(skill ? { facets: ['skills.skill'] } : {}), // \"facets\" param sent to fetch Recommendations row courses\n },\n });\n\n return {\n hits: hits as AlgoliaProductHit[],\n nbHits: nbHits ?? 0,\n nbPages: nbPages ?? 0,\n };\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n logError(err);\n return { hits: [], nbHits: 0, nbPages: 0 };\n }\n};\n\n// ─── fetchPopularProducts ─────────────────────────────────────────────────────\n\n/**\n * Fetches top-ranked products with no query or tag filters.\n * Used for the \"Popular programs / courses\" fallback in the no-results state.\n *\n * @param searchClient Algolia client\n * @param productType 'program' | 'course'\n * @param hitsPerPage Number of items to fetch\n * @returns Raw Algolia hit objects\n */\nexport const fetchPopularProducts = async (\n searchClient: SearchClient,\n productType: ProductType,\n hitsPerPage: number,\n skill?: Skill,\n): Promise<AlgoliaProductHit[]> => {\n const { hits } = await searchProducts(\n searchClient,\n productType,\n '', // empty query → Algolia ranks by popularity\n [], // no tag filters\n FACET_SUBJECTS,\n hitsPerPage,\n 0,\n ['b2c_subscription_inclusion:true'],\n skill,\n );\n return hits;\n};\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PROGRESSIVE_PROFILING_USER_STATUS } from '../../../widgets/ProgressiveProfilingQuiz/data/constants';
|
|
3
|
+
import { RecommendationsRailsPanel } from '../../../containers/RecommendationsRailsPanel';
|
|
4
|
+
import PopularCoursePanel from '../../../containers/PopularCoursePanel';
|
|
5
|
+
import PopularProgramsPanel from '../../../containers/PopularProgramsPanel';
|
|
6
|
+
import { useProgressiveProfilingQuiz } from '../ProgressiveProfilingQuizProvider';
|
|
7
|
+
const DataRailSelection = () => {
|
|
8
|
+
const { progressiveProfileStatus, } = useProgressiveProfilingQuiz();
|
|
9
|
+
if (!progressiveProfileStatus)
|
|
10
|
+
return null;
|
|
11
|
+
const { status } = progressiveProfileStatus || {};
|
|
12
|
+
if (status === PROGRESSIVE_PROFILING_USER_STATUS.COMPLETE) {
|
|
13
|
+
return _jsx(RecommendationsRailsPanel, {});
|
|
14
|
+
}
|
|
15
|
+
if (status !== PROGRESSIVE_PROFILING_USER_STATUS.COMPLETE) {
|
|
16
|
+
return (_jsxs(_Fragment, { children: [_jsx(PopularCoursePanel, {}), _jsx(PopularProgramsPanel, {})] }));
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
};
|
|
20
|
+
export default DataRailSelection;
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/widgets/ProgressiveProfilingQuiz/DataRailSelection/index.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,iCAAiC,EAAE,MAAM,sDAAsD,CAAC;AACzG,OAAO,EAAE,yBAAyB,EAAE,MAAM,+CAA+C,CAAC;AAC1F,OAAO,kBAAkB,MAAM,oCAAoC,CAAC;AACpE,OAAO,oBAAoB,MAAM,sCAAsC,CAAC;AACxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAElF,MAAM,iBAAiB,GAAa,GAAG,EAAE;IACvC,MAAM,EACJ,wBAAwB,GACzB,GAAG,2BAA2B,EAAE,CAAC;IAElC,IAAI,CAAC,wBAAwB;QAAE,OAAO,IAAI,CAAC;IAE3C,MAAM,EAAE,MAAM,EAAE,GAAG,wBAAwB,IAAI,EAAE,CAAC;IAElD,IAAI,MAAM,KAAK,iCAAiC,CAAC,QAAQ,EAAE,CAAC;QAC1D,OAAO,KAAC,yBAAyB,KAAG,CAAC;IACvC,CAAC;IAED,IAAI,MAAM,KAAK,iCAAiC,CAAC,QAAQ,EAAE,CAAC;QAC1D,OAAO,CACL,8BACE,KAAC,kBAAkB,KAAG,EACtB,KAAC,oBAAoB,KAAG,IACvB,CACJ,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,eAAe,iBAAiB,CAAC","sourcesContent":["import React from 'react';\nimport { PROGRESSIVE_PROFILING_USER_STATUS } from '@src/widgets/ProgressiveProfilingQuiz/data/constants';\nimport { RecommendationsRailsPanel } from '../../../containers/RecommendationsRailsPanel';\nimport PopularCoursePanel from '@src/containers/PopularCoursePanel';\nimport PopularProgramsPanel from '@src/containers/PopularProgramsPanel';\nimport { useProgressiveProfilingQuiz } from '../ProgressiveProfilingQuizProvider';\n\nconst DataRailSelection: React.FC = () => {\n const {\n progressiveProfileStatus,\n } = useProgressiveProfilingQuiz();\n\n if (!progressiveProfileStatus) return null;\n\n const { status } = progressiveProfileStatus || {};\n\n if (status === PROGRESSIVE_PROFILING_USER_STATUS.COMPLETE) {\n return <RecommendationsRailsPanel />;\n }\n\n if (status !== PROGRESSIVE_PROFILING_USER_STATUS.COMPLETE) {\n return (\n <>\n <PopularCoursePanel />\n <PopularProgramsPanel />\n </>\n );\n }\n return null;\n};\n\nexport default DataRailSelection;\n"]}
|
|
@@ -2,6 +2,7 @@ import { WidgetOperationTypes } from '@openedx/frontend-base';
|
|
|
2
2
|
import { ProgressiveProfilingQuizProvider } from './ProgressiveProfilingQuizProvider';
|
|
3
3
|
import ProgressiveProfilingQuizModal from './modal';
|
|
4
4
|
import { BannerEntry } from './SearchPageBanners';
|
|
5
|
+
import DataRailSelection from './DataRailSelection';
|
|
5
6
|
const app = {
|
|
6
7
|
appId: 'org.2u-edx.frontend.app.progressiveProfiling',
|
|
7
8
|
providers: [
|
|
@@ -21,6 +22,13 @@ const app = {
|
|
|
21
22
|
op: WidgetOperationTypes.APPEND,
|
|
22
23
|
component: BannerEntry,
|
|
23
24
|
},
|
|
25
|
+
{
|
|
26
|
+
// This is for recommendations row slot, which is used in the search page also.
|
|
27
|
+
slotId: 'org.edx.frontend.widget.recommendations.v1',
|
|
28
|
+
id: 'org.edx.frontend.widget.progressiveProfiling.recommendations.v1',
|
|
29
|
+
op: WidgetOperationTypes.PREPEND,
|
|
30
|
+
component: DataRailSelection,
|
|
31
|
+
},
|
|
24
32
|
]
|
|
25
33
|
};
|
|
26
34
|
export default app;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../../src/widgets/ProgressiveProfilingQuiz/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,6BAA6B,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../../src/widgets/ProgressiveProfilingQuiz/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,6BAA6B,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AAEpD,MAAM,GAAG,GAAQ;IACf,KAAK,EAAE,8CAA8C;IACrD,SAAS,EAAE;QACT,gCAAgC;KACjC;IACD,KAAK,EAAE;QACL;YACE,MAAM,EAAE,0CAA0C;YAClD,EAAE,EAAE,uDAAuD;YAC3D,EAAE,EAAE,oBAAoB,CAAC,OAAO;YAChC,SAAS,EAAE,6BAA6B;SACzC;QACD;YACE,6DAA6D;YAC7D,MAAM,EAAE,4DAA4D;YACpE,EAAE,EAAE,kEAAkE;YACtE,EAAE,EAAE,oBAAoB,CAAC,MAAM;YAC/B,SAAS,EAAE,WAAW;SACvB;QACD;YACE,+EAA+E;YAC/E,MAAM,EAAE,4CAA4C;YACpD,EAAE,EAAE,iEAAiE;YACrE,EAAE,EAAE,oBAAoB,CAAC,OAAO;YAChC,SAAS,EAAE,iBAAiB;SAC7B;KACF;CACF,CAAC;AAEF,eAAe,GAAG,CAAC","sourcesContent":["import { App, WidgetOperationTypes } from '@openedx/frontend-base';\nimport { ProgressiveProfilingQuizProvider } from './ProgressiveProfilingQuizProvider';\nimport ProgressiveProfilingQuizModal from './modal';\nimport { BannerEntry } from './SearchPageBanners';\nimport DataRailSelection from './DataRailSelection';\n\nconst app: App = {\n appId: 'org.2u-edx.frontend.app.progressiveProfiling',\n providers: [\n ProgressiveProfilingQuizProvider,\n ],\n slots: [\n {\n slotId: 'org.openedx.frontend.slot.header.main.v1',\n id: 'org.edx.frontend.widget.progressiveProfiling.modal.v1',\n op: WidgetOperationTypes.PREPEND,\n component: ProgressiveProfilingQuizModal,\n },\n {\n // This is the entry-point CTA injected into search page slot\n slotId: 'org.edx.frontend.widget.progressiveProfiling.search.cta.v1',\n id: 'org.edx.frontend.widget.progressiveProfiling.search.cta.entry.v1',\n op: WidgetOperationTypes.APPEND,\n component: BannerEntry,\n },\n {\n // This is for recommendations row slot, which is used in the search page also.\n slotId: 'org.edx.frontend.widget.recommendations.v1',\n id: 'org.edx.frontend.widget.progressiveProfiling.recommendations.v1',\n op: WidgetOperationTypes.PREPEND,\n component: DataRailSelection,\n },\n ]\n};\n\nexport default app;\n"]}
|