Skip to content

Commit 3f92e5b

Browse files
jkachelgumaerc
authored andcommitted
Adds (really pretty ugly) interstitial page, routing, and cobbles together a sample API hook
The hook does hit the right endpoint but it's a stand-in until the API PR gets merged.
1 parent 6d72b9d commit 3f92e5b

File tree

7 files changed

+159
-6
lines changed

7 files changed

+159
-6
lines changed

frontends/api/src/mitxonline/clients.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
UsersApi,
1010
} from "@mitodl/mitxonline-api-axios/v2"
1111
import axios from "axios"
12+
import { AxiosInstance } from "axios"
1213

1314
const axiosInstance = axios.create({
1415
baseURL: process.env.NEXT_PUBLIC_MITX_ONLINE_BASE_URL,
@@ -22,6 +23,22 @@ const axiosInstance = axios.create({
2223
const BASE_PATH =
2324
process.env.NEXT_PUBLIC_MITX_ONLINE_BASE_URL?.replace(/\/+$/, "") ?? ""
2425

26+
class B2BAttachApi {
27+
private axiosInstance: AxiosInstance;
28+
private basePath: string;
29+
30+
constructor(_config: unknown = undefined, basePath: string, axiosInstance: AxiosInstance) {
31+
this.basePath = basePath;
32+
this.axiosInstance = axiosInstance;
33+
}
34+
35+
async attach(code: string, data?: unknown, options?: unknown) {
36+
const url = `${this.basePath}/api/v0/b2b/attach/${encodeURIComponent(code)}/`;
37+
return this.axiosInstance.post(url, data, options || undefined);
38+
}
39+
}
40+
41+
2542
const usersApi = new UsersApi(undefined, BASE_PATH, axiosInstance)
2643
const b2bApi = new B2bApi(undefined, BASE_PATH, axiosInstance)
2744
const programsApi = new ProgramsApi(undefined, BASE_PATH, axiosInstance)
@@ -38,6 +55,7 @@ const programCertificatesApi = new ProgramCertificatesApi(
3855
)
3956

4057
const coursesApi = new CoursesApi(undefined, BASE_PATH, axiosInstance)
58+
const b2bAttachApi = new B2BAttachApi(undefined, BASE_PATH, axiosInstance);
4159

4260
const courseCertificatesApi = new CourseCertificatesApi(
4361
undefined,
@@ -55,6 +73,7 @@ export {
5573
usersApi,
5674
b2bApi,
5775
courseRunEnrollmentsApi,
76+
b2bAttachApi,
5877
programsApi,
5978
programCollectionsApi,
6079
coursesApi,
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
import { organizationQueries } from "./queries"
1+
import { organizationQueries, useB2BAttachMutation } from "./queries"
22

3-
export { organizationQueries }
3+
export { organizationQueries, useB2BAttachMutation }

frontends/api/src/mitxonline/hooks/organizations/queries.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { B2bApiB2bOrganizationsRetrieveRequest } from "@mitodl/mitxonline-api-axios/v0"
2-
import { OrganizationPage } from "@mitodl/mitxonline-api-axios/v2"
3-
import { queryOptions } from "@tanstack/react-query"
4-
import { b2bApi } from "../../clients"
2+
import { OrganizationPage } from "@mitodl/mitxonline-api-axios/v1"
3+
import { queryOptions, useMutation } from "@tanstack/react-query"
4+
import { b2bApi, b2bAttachApi } from "../../clients"
55

66
const organizationKeys = {
77
root: ["mitxonline", "organizations"],
@@ -22,4 +22,12 @@ const organizationQueries = {
2222
}),
2323
}
2424

25-
export { organizationQueries, organizationKeys }
25+
const useB2BAttachMutation = (code: string) => {
26+
return useMutation({
27+
mutationFn: (data?: unknown) => {
28+
return b2bAttachApi.attach(code, data).then((res) => res.data)
29+
},
30+
})
31+
}
32+
33+
export { organizationQueries, organizationKeys, useB2BAttachMutation }
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"use client"
2+
import React from "react"
3+
import { Breadcrumbs, Container, Typography } from "ol-components"
4+
import * as urls from "@/common/urls"
5+
import { useB2BAttachMutation } from "api/mitxonline-hooks/organizations"
6+
7+
type B2BAttachPageProps = {
8+
code: string;
9+
};
10+
11+
const B2BAttachPage: React.FC<B2BAttachPageProps> = ({ code }) => {
12+
// This is an interstitial. The user sits here until we validate the code,
13+
// then we toss them to the dashboard.
14+
15+
const { mutate: attach, isSuccess } = useB2BAttachMutation(code)
16+
17+
React.useEffect(() => {
18+
// Trigger the attach mutation when the component mounts
19+
console.log("attempting to attach code", code)
20+
attach(code)
21+
}, [attach, code])
22+
23+
if (isSuccess) {
24+
//window.history.replaceState(null, "", urls.DASHBOARD_HOME);
25+
}
26+
27+
return (
28+
<Container>
29+
<Breadcrumbs
30+
variant="light"
31+
ancestors={[{ href: urls.HOME, label: "Home" }]}
32+
current="Use Enrollment Code"
33+
/>
34+
35+
<Typography component="h1" variant="h3">
36+
Please Wait
37+
</Typography>
38+
39+
<Typography>
40+
{isSuccess ? <>Your enrollment code {code} has been validated.</> : <>Please wait while we validate your enrollment code {code}</>}
41+
</Typography>
42+
</Container>
43+
)
44+
}
45+
46+
export default B2BAttachPage
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import React from "react"
2+
import { renderWithProviders, setMockResponse } from "@/test-utils"
3+
import { urls } from "api/test-utils"
4+
import * as commonUrls from "@/common/urls"
5+
import { ForbiddenError } from "@/common/errors"
6+
import { Permission } from "api/hooks/user"
7+
import { useFeatureFlagEnabled } from "posthog-js/react"
8+
import CartPage from "./CartPage"
9+
import { allowConsoleErrors } from "ol-test-utilities"
10+
11+
jest.mock("posthog-js/react")
12+
const mockedUseFeatureFlagEnabled = jest.mocked(useFeatureFlagEnabled)
13+
14+
const oldWindowLocation = window.location
15+
16+
beforeAll(() => {
17+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
18+
delete (window as any).location
19+
20+
window.location = Object.defineProperties({} as Location, {
21+
...Object.getOwnPropertyDescriptors(oldWindowLocation),
22+
assign: {
23+
configurable: true,
24+
value: jest.fn(),
25+
},
26+
})
27+
})
28+
29+
afterAll(() => {
30+
window.location = oldWindowLocation
31+
})
32+
33+
describe("CartPage", () => {
34+
;["on", "off"].forEach((testCase: string) => {
35+
test(`Renders when logged in and feature flag is ${testCase}`, async () => {
36+
setMockResponse.get(urls.userMe.get(), {
37+
[Permission.Authenticated]: true,
38+
})
39+
mockedUseFeatureFlagEnabled.mockReturnValue(testCase === "on")
40+
41+
if (testCase === "off") {
42+
allowConsoleErrors()
43+
expect(() =>
44+
renderWithProviders(<CartPage />, {
45+
url: commonUrls.ECOMMERCE_CART,
46+
}),
47+
).toThrow(ForbiddenError)
48+
} else {
49+
renderWithProviders(<CartPage />, {
50+
url: commonUrls.ECOMMERCE_CART,
51+
})
52+
}
53+
})
54+
})
55+
})
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React from "react"
2+
import { Metadata } from "next"
3+
import { standardizeMetadata } from "@/common/metadata"
4+
import invariant from "tiny-invariant"
5+
6+
import type { PageParams } from "@/app/types"
7+
import B2BAttachPage from "@/app-pages/B2BAttachPage/B2BAttachPage"
8+
9+
export const metadata: Metadata = standardizeMetadata({
10+
title: "Use Enrollment Code",
11+
})
12+
13+
const Page: React.FC<PageParams<object, { code: string }>> = async ({
14+
params
15+
}) => {
16+
const resolved = await params
17+
invariant(resolved?.code, "code is required")
18+
return <B2BAttachPage code={resolved?.code} />
19+
}
20+
21+
export default Page

frontends/main/src/common/urls.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,7 @@ export const SEARCH_LEARNING_MATERIAL = querifiedSearchUrl({
167167
})
168168

169169
export const ECOMMERCE_CART = "/cart/" as const
170+
171+
export const B2B_ATTACH_VIEW = "/attach/[code]"
172+
export const b2bAttachView = (code: string) =>
173+
generatePath(B2B_ATTACH_VIEW, { code: code })

0 commit comments

Comments
 (0)