Skip to content

Commit c92adf0

Browse files
jonkaftonodlbotgumaercrenovate[bot]shanbady
authored
Server render search results (search and channel pages) (#1833)
* Release 0.24.3 * Release date for 0.24.3 * Server rendered search page results * v2 drawer certification updates (#1823) * update certification display in v2 drawer to match latest designs * don't show price info item if runs have differing data * MicroMasters not Micromasters * if there is no price for the certificate but it's indicated that one is included, display that * if resource is free, includes a certification but has no prices, still display the pill in the info item * generate migration for MicroMasters spelling change * fix certificate pill padding on mobile * Unit channel page and search prefetch * Featured list and testimonials only for unit channels * v2 learning resource drawer formats and location (#1826) * add format info item * display location if format is in_person * add tests * also show location for hybrid courses * LocalDate and NoSSR components to render localized dates only on client * Revert "LocalDate and NoSSR components to render localized dates only on client" This reverts commit b4ccd6d. * LocalDate and NoSSR components to render localized dates only on client (#1831) * LocalDate and NoSSR components to render localized dates only on client * Remove unnecessary React.Fragment * separate starts and as taught in, show anytime availability (#1828) * refactor starts / as taught in functionality to show on separate lines, show "anytime" in starts if availability is anytime * fix rebase mishap * Map address search params * Search params test * Update dependency pytest-cov to v6 (#1818) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Update dependency safety to v3 (#1819) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * URL search param validation anf transforms to align with course-search-utils * Update dependency django-anymail to v12 (#1815) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * vector search endpoint (#1827) * adding initial vector search view * adding working vector results endpoint * regenerate openapi spec * fixing format of returned results * adding test * patching qdrant client * moving to v0 api * switch to custom serializer for vector search * fix v0 url * using minimal serializer * returning minimal response for vector results * regenerate spec * adding some other useful bits to response * fixing response for empty query and adjusting test * regenerate spec * uninheriting from searchrequest serializer * updating oai spec * updating oai spec * Update dependency @mui/lab to v6.0.0-beta.15 (#1830) * Update dependency @mui/lab to v6.0.0-beta.15 * update lockfile --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: shankar ambady <[email protected]> * Update to use validators from course-search-utils --------- Co-authored-by: Doof <[email protected]> Co-authored-by: Carey P Gumaer <[email protected]> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Shankar Ambady <[email protected]> Co-authored-by: shankar ambady <[email protected]>
1 parent f4b10d6 commit c92adf0

File tree

16 files changed

+517
-245
lines changed

16 files changed

+517
-245
lines changed

frontends/api/src/ssr/prefetch.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ export const prefetch = async (
1212
queryClient = queryClient || new QueryClient()
1313

1414
await Promise.all(
15-
queries.map((query) => queryClient.prefetchQuery(query as Query)),
15+
queries
16+
.filter(Boolean)
17+
.map((query) => queryClient.prefetchQuery(query as Query)),
1618
)
1719

1820
return { dehydratedState: dehydrate(queryClient), queryClient }

frontends/main/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"dependencies": {
1313
"@ebay/nice-modal-react": "^1.2.13",
1414
"@emotion/cache": "^11.13.1",
15-
"@mitodl/course-search-utils": "^3.3.1",
15+
"@mitodl/course-search-utils": "git://github.com/mitodl/course-search-utils.git#jk/6035-export-validation",
1616
"@next/bundle-analyzer": "^14.2.15",
1717
"@remixicon/react": "^4.2.0",
1818
"@sentry/nextjs": "^8.36.0",

frontends/main/src/app-pages/ChannelPage/ChannelPage.tsx

+6-19
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,9 @@ import { useParams } from "next/navigation"
66
import { ChannelPageTemplate } from "./ChannelPageTemplate"
77
import { useChannelDetail } from "api/hooks/channels"
88
import ChannelSearch from "./ChannelSearch"
9-
import type {
10-
Facets,
11-
FacetKey,
12-
BooleanFacets,
13-
} from "@mitodl/course-search-utils"
149
import { ChannelTypeEnum } from "api/v0"
1510
import { Typography } from "ol-components"
11+
import { getConstantSearchParams } from "./searchRequests"
1612

1713
type RouteParams = {
1814
channelType: ChannelTypeEnum
@@ -22,20 +18,11 @@ type RouteParams = {
2218
const ChannelPage: React.FC = () => {
2319
const { channelType, name } = useParams<RouteParams>()
2420
const channelQuery = useChannelDetail(String(channelType), String(name))
25-
const searchParams: Facets & BooleanFacets = {}
2621
const publicDescription = channelQuery.data?.public_description
2722

28-
if (channelQuery.data?.search_filter) {
29-
const urlParams = new URLSearchParams(channelQuery.data.search_filter)
30-
for (const [key, value] of urlParams.entries()) {
31-
const paramEntry = searchParams[key as FacetKey]
32-
if (paramEntry !== undefined) {
33-
paramEntry.push(value)
34-
} else {
35-
searchParams[key as FacetKey] = [value]
36-
}
37-
}
38-
}
23+
const channelSearchFilter = channelQuery.data?.search_filter
24+
25+
const searchParams = getConstantSearchParams(channelSearchFilter)
3926

4027
return (
4128
name &&
@@ -46,9 +33,9 @@ const ChannelPage: React.FC = () => {
4633
{publicDescription && (
4734
<Typography variant="body1">{publicDescription}</Typography>
4835
)}
49-
{channelQuery.data?.search_filter && (
36+
{channelSearchFilter && (
5037
<ChannelSearch
51-
channelTitle={channelQuery.data.title}
38+
channelTitle={channelQuery.data!.title}
5239
constantSearchParams={searchParams}
5340
channelType={channelType}
5441
/>

frontends/main/src/app-pages/ChannelPage/ChannelSearch.tsx

+5-78
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
11
import React, { useCallback, useMemo } from "react"
2-
import { LearningResourceOfferor } from "api"
32
import { ChannelTypeEnum } from "api/v0"
43
import { useOfferorsList } from "api/hooks/learningResources"
5-
6-
import {
7-
useResourceSearchParams,
8-
UseResourceSearchParamsProps,
9-
} from "@mitodl/course-search-utils"
10-
import type {
11-
Facets,
12-
BooleanFacets,
13-
FacetManifest,
14-
} from "@mitodl/course-search-utils"
4+
import { useResourceSearchParams } from "@mitodl/course-search-utils"
5+
import type { Facets, BooleanFacets } from "@mitodl/course-search-utils"
156
import { useSearchParams } from "@mitodl/course-search-utils/next"
167
import SearchDisplay from "@/page-components/SearchDisplay/SearchDisplay"
178
import { Container, styled, VisuallyHidden } from "ol-components"
189
import { SearchField } from "@/page-components/SearchField/SearchField"
19-
20-
import { getFacetManifest } from "@/app-pages/SearchPage/SearchPage"
10+
import { getFacets } from "./searchRequests"
2111

2212
import _ from "lodash"
2313

@@ -35,34 +25,6 @@ const StyledSearchField = styled(SearchField)({
3525
width: "624px",
3626
})
3727

38-
const FACETS_BY_CHANNEL_TYPE: Record<ChannelTypeEnum, string[]> = {
39-
[ChannelTypeEnum.Topic]: [
40-
"free",
41-
"resource_type",
42-
"certification_type",
43-
"delivery",
44-
"offered_by",
45-
"department",
46-
],
47-
[ChannelTypeEnum.Department]: [
48-
"free",
49-
"resource_type",
50-
"certification_type",
51-
"topic",
52-
"delivery",
53-
"offered_by",
54-
],
55-
[ChannelTypeEnum.Unit]: [
56-
"free",
57-
"resource_type",
58-
"topic",
59-
"certification_type",
60-
"delivery",
61-
"department",
62-
],
63-
[ChannelTypeEnum.Pathway]: [],
64-
}
65-
6628
const SHOW_PROFESSIONAL_TOGGLE_BY_CHANNEL_TYPE: Record<
6729
ChannelTypeEnum,
6830
boolean
@@ -73,24 +35,6 @@ const SHOW_PROFESSIONAL_TOGGLE_BY_CHANNEL_TYPE: Record<
7335
[ChannelTypeEnum.Pathway]: false,
7436
}
7537

76-
const getFacetManifestForChannelType = (
77-
channelType: ChannelTypeEnum,
78-
offerors: Record<string, LearningResourceOfferor>,
79-
constantSearchParams: Facets,
80-
resourceCategory: string | null,
81-
): FacetManifest => {
82-
const facets = FACETS_BY_CHANNEL_TYPE[channelType] || []
83-
return getFacetManifest(offerors, resourceCategory)
84-
.filter(
85-
(facetSetting) =>
86-
!Object.keys(constantSearchParams).includes(facetSetting.name) &&
87-
facets.includes(facetSetting.name),
88-
)
89-
.sort(
90-
(a, b) => facets.indexOf(a.name) - facets.indexOf(b.name),
91-
) as FacetManifest
92-
}
93-
9438
interface ChannelSearchProps {
9539
constantSearchParams: Facets & BooleanFacets
9640
channelType: ChannelTypeEnum
@@ -110,14 +54,9 @@ const ChannelSearch: React.FC<ChannelSearchProps> = ({
11054
const [searchParams, setSearchParams] = useSearchParams()
11155
const resourceCategory = searchParams.get("resource_category")
11256

113-
const facetManifest = useMemo(
57+
const { facetNames, facetManifest } = useMemo(
11458
() =>
115-
getFacetManifestForChannelType(
116-
channelType,
117-
offerors,
118-
constantSearchParams,
119-
resourceCategory,
120-
),
59+
getFacets(channelType, offerors, constantSearchParams, resourceCategory),
12160
[offerors, channelType, constantSearchParams, resourceCategory],
12261
)
12362

@@ -140,18 +79,6 @@ const ChannelSearch: React.FC<ChannelSearchProps> = ({
14079
setPage(1)
14180
}, [setPage])
14281

143-
const facetNames = Array.from(
144-
new Set(
145-
facetManifest.flatMap((facet) => {
146-
if (facet.type === "group") {
147-
return facet.facets.map((subfacet) => subfacet.name)
148-
} else {
149-
return [facet.name]
150-
}
151-
}),
152-
),
153-
) as UseResourceSearchParamsProps["facets"]
154-
15582
const {
15683
hasFacets,
15784
params,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import type {
2+
Facets,
3+
FacetKey,
4+
BooleanFacets,
5+
FacetManifest,
6+
UseResourceSearchParamsProps,
7+
} from "@mitodl/course-search-utils"
8+
import { LearningResourceOfferor } from "api"
9+
import { ChannelTypeEnum } from "api/v0"
10+
import { getFacetManifest } from "@/page-components/SearchDisplay/getFacetManifest"
11+
12+
export const getConstantSearchParams = (searchFilter?: string) => {
13+
const searchParams: Facets & BooleanFacets = {}
14+
15+
if (searchFilter) {
16+
const urlParams = new URLSearchParams(searchFilter)
17+
for (const [key, value] of urlParams.entries()) {
18+
const paramEntry = searchParams[key as FacetKey]
19+
if (paramEntry !== undefined) {
20+
paramEntry.push(value)
21+
} else {
22+
searchParams[key as FacetKey] = [value]
23+
}
24+
}
25+
}
26+
27+
return searchParams
28+
}
29+
30+
const FACETS_BY_CHANNEL_TYPE: Record<ChannelTypeEnum, string[]> = {
31+
[ChannelTypeEnum.Topic]: [
32+
"free",
33+
"resource_type",
34+
"certification_type",
35+
"delivery",
36+
"offered_by",
37+
"department",
38+
],
39+
[ChannelTypeEnum.Department]: [
40+
"free",
41+
"resource_type",
42+
"certification_type",
43+
"topic",
44+
"delivery",
45+
"offered_by",
46+
],
47+
[ChannelTypeEnum.Unit]: [
48+
"free",
49+
"resource_type",
50+
"topic",
51+
"certification_type",
52+
"delivery",
53+
"department",
54+
],
55+
[ChannelTypeEnum.Pathway]: [],
56+
}
57+
58+
const getFacetManifestForChannelType = (
59+
channelType: ChannelTypeEnum,
60+
offerors: Record<string, LearningResourceOfferor>,
61+
constantSearchParams: Facets,
62+
resourceCategory: string | null,
63+
): FacetManifest => {
64+
const facets = FACETS_BY_CHANNEL_TYPE[channelType] || []
65+
return getFacetManifest(offerors, resourceCategory)
66+
.filter(
67+
(facetSetting) =>
68+
!Object.keys(constantSearchParams).includes(facetSetting.name) &&
69+
facets.includes(facetSetting.name),
70+
)
71+
.sort(
72+
(a, b) => facets.indexOf(a.name) - facets.indexOf(b.name),
73+
) as FacetManifest
74+
}
75+
76+
export const getFacets = (
77+
channelType: ChannelTypeEnum,
78+
offerors: Record<string, LearningResourceOfferor>,
79+
constantSearchParams: Facets,
80+
resourceCategory: string | null,
81+
) => {
82+
const facetManifest = getFacetManifestForChannelType(
83+
channelType,
84+
offerors,
85+
constantSearchParams,
86+
resourceCategory,
87+
)
88+
89+
const facetNames = Array.from(
90+
new Set(
91+
facetManifest.flatMap((facet) => {
92+
if (facet.type === "group") {
93+
return facet.facets.map((subFacet) => subFacet.name)
94+
} else {
95+
return [facet.name]
96+
}
97+
}),
98+
),
99+
) as UseResourceSearchParamsProps["facets"]
100+
101+
return { facetNames, facetManifest }
102+
}

0 commit comments

Comments
 (0)