Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -728,4 +728,14 @@ describe("OrganizationContent", () => {
expect(cardStatus).toHaveTextContent("Not Enrolled")
}
})

test("shows the not found screen if the organization is not found by orgSlug", async () => {
const { mitxOnlineUser } = setupOrgAndUser()

setMockResponse.get(urls.userMe.get(), mitxOnlineUser)

renderWithProviders(<OrganizationContent orgSlug="not-found" />)

await screen.findByRole("heading", { name: "Organization not found" })
})
})
45 changes: 33 additions & 12 deletions frontends/main/src/app-pages/DashboardPage/OrganizationContent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client"

import React from "react"
import React, { useEffect } from "react"
import DOMPurify from "isomorphic-dompurify"
import Image from "next/image"
import { useFeatureFlagEnabled } from "posthog-js/react"
Expand All @@ -27,9 +27,10 @@ import {
OrganizationPage,
UserProgramEnrollmentDetail,
} from "@mitodl/mitxonline-api-axios/v2"
import { useMitxOnlineUserMe } from "api/mitxonline-hooks/user"
import { mitxUserQueries } from "api/mitxonline-hooks/user"
import { ButtonLink } from "@mitodl/smoot-design"
import { RiAwardFill } from "@remixicon/react"
import { ErrorContent } from "../ErrorPage/ErrorPageTemplate"

const HeaderRoot = styled.div({
display: "flex",
Expand Down Expand Up @@ -426,24 +427,44 @@ const OrganizationContentInternal: React.FC<
)
}

const matchOrganizationBySlug =
(orgSlug: string) => (organization: OrganizationPage) => {
return organization.slug.replace("org-", "") === orgSlug
}

type OrganizationContentProps = {
orgSlug: string
}
const OrganizationContent: React.FC<OrganizationContentProps> = ({
orgSlug,
}) => {
const { isLoading: isLoadingMitxOnlineUser, data: mitxOnlineUser } =
useMitxOnlineUserMe()
const b2bOrganization = mitxOnlineUser?.b2b_organizations.find(
(org) => org.slug.replace("org-", "") === orgSlug,
const { isLoading: isLoadingMitxOnlineUser, data: mitxOnlineUser } = useQuery(
mitxUserQueries.me(),
)
const skeleton = (
<Skeleton width="100%" height="100px" style={{ marginBottom: "16px" }} />

useEffect(() => {
if (
mitxOnlineUser?.b2b_organizations.find(matchOrganizationBySlug(orgSlug))
) {
localStorage.setItem("last-dashboard-org", orgSlug)
}
}, [mitxOnlineUser, orgSlug])

if (isLoadingMitxOnlineUser) {
return (
<Skeleton width="100%" height="100px" style={{ marginBottom: "16px" }} />
)
}

const b2bOrganization = mitxOnlineUser?.b2b_organizations.find(
matchOrganizationBySlug(orgSlug),
)
if (isLoadingMitxOnlineUser || isLoadingMitxOnlineUser) return skeleton
return b2bOrganization ? (
<OrganizationContentInternal org={b2bOrganization} />
) : null

if (!b2bOrganization) {
return <ErrorContent title="Organization not found" timSays="404" />
}

return <OrganizationContentInternal org={b2bOrganization} />
}

export default OrganizationContent
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from "react"
import { renderWithProviders, waitFor } from "@/test-utils"
import OrganizationRedirect from "./OrganizationRedirect"
import { setMockResponse } from "api/test-utils"
import { urls, factories } from "api/mitxonline-test-utils"
import { setupOrgAndUser } from "./CoursewareDisplay/test-utils"

jest.mock("next-nprogress-bar", () => ({
useRouter: jest.fn(),
}))

const mockReplace = jest.fn()

const { useRouter } = jest.requireMock("next-nprogress-bar")
useRouter.mockReturnValue({
replace: mockReplace,
})

describe("OrganizationRedirect", () => {
beforeEach(() => {
mockReplace.mockClear()
localStorage.clear()
setMockResponse.get(urls.enrollment.enrollmentsList(), [])
setMockResponse.get(urls.programEnrollments.enrollmentsList(), [])
setMockResponse.get(urls.contracts.contractsList(), [])
})

test("navigates to user's first organization", async () => {
const { mitxOnlineUser } = setupOrgAndUser()

const userWithTwoOrgs = {
...mitxOnlineUser,
b2b_organizations: [
mitxOnlineUser.b2b_organizations[0],
factories.organizations.organization({}),
],
}

setMockResponse.get(urls.userMe.get(), userWithTwoOrgs)

renderWithProviders(<OrganizationRedirect />)

await waitFor(() => {
expect(mockReplace).toHaveBeenCalledWith(
`/dashboard/organization/${mitxOnlineUser.b2b_organizations[0].slug.replace("org-", "")}`,
)
})
})

test("navigates to user's last visited organization", async () => {
const { mitxOnlineUser } = setupOrgAndUser()
setMockResponse.get(urls.userMe.get(), mitxOnlineUser)
localStorage.setItem("last-dashboard-org", "last-visited-org")
renderWithProviders(<OrganizationRedirect />)
await waitFor(() => {
expect(mockReplace).toHaveBeenCalledWith(
"/dashboard/organization/last-visited-org",
)
})
})

test("navigates to dashboard home if user has no organization", async () => {
const { mitxOnlineUser } = setupOrgAndUser()

const userWithNoOrgs = {
...mitxOnlineUser,
b2b_organizations: [],
}

setMockResponse.get(urls.userMe.get(), userWithNoOrgs)

renderWithProviders(<OrganizationRedirect />)

await waitFor(() => {
expect(mockReplace).toHaveBeenCalledWith("/dashboard")
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"use client"

import React, { useEffect } from "react"
import { useRouter } from "next-nprogress-bar"
import { useQuery } from "@tanstack/react-query"
import { mitxUserQueries } from "api/mitxonline-hooks/user"
import { Skeleton } from "ol-components"

const OrganizationRedirect: React.FC = () => {
const router = useRouter()

const { isLoading: isLoadingMitxOnlineUser, data: mitxOnlineUser } = useQuery(
mitxUserQueries.me(),
)

useEffect(() => {
if (!isLoadingMitxOnlineUser) {
if (mitxOnlineUser) {
const b2bOrganization = mitxOnlineUser.b2b_organizations[0]
if (b2bOrganization) {
const lastVisited = localStorage.getItem("last-dashboard-org")
if (lastVisited) {
router.replace(`/dashboard/organization/${lastVisited}`)
} else {
router.replace(
`/dashboard/organization/${b2bOrganization.slug.replace("org-", "")}`,
)
}
} else {
router.replace("/dashboard")
}
} else {
router.replace("/dashboard")
}
}
}, [isLoadingMitxOnlineUser, mitxOnlineUser, router])

if (isLoadingMitxOnlineUser) {
return (
<Skeleton width="100%" height="100px" style={{ marginBottom: "16px" }} />
)
}

return null
}

export default OrganizationRedirect
47 changes: 28 additions & 19 deletions frontends/main/src/app-pages/ErrorPage/ErrorPageTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,33 @@ const Button = styled(ButtonLink)({
minWidth: "200px",
})

export const ErrorContent: React.FC<ErrorPageTemplateProps> = ({
title,
timSays,
}) => {
return (
<ErrorContainer>
<ImageContainer>
<TimSpeechImage src={timSpeechBubble} alt="" />
<TimSays>{timSays || "Oops!"}</TimSays>
<TimImage src={timImage} alt="" />
</ImageContainer>
<Typography
component="h1"
variant="h3"
sx={{ textAlign: "center", margin: "0 30px" }}
>
{title}
</Typography>
<Footer>
<Button variant="primary" href={HOME} Component="a">
Home
</Button>
</Footer>
</ErrorContainer>
)
}

const ErrorPageTemplate: React.FC<ErrorPageTemplateProps> = ({
title,
timSays,
Expand Down Expand Up @@ -109,25 +136,7 @@ const ErrorPageTemplate: React.FC<ErrorPageTemplateProps> = ({
}
return (
<Page>
<ErrorContainer>
<ImageContainer>
<TimSpeechImage src={timSpeechBubble} alt="" />
<TimSays>{timSays || "Oops!"}</TimSays>
<TimImage src={timImage} alt="" />
</ImageContainer>
<Typography
component="h1"
variant="h3"
sx={{ textAlign: "center", margin: "0 30px" }}
>
{title}
</Typography>
<Footer>
<Button variant="primary" href={HOME} Component="a">
Home
</Button>
</Footer>
</ErrorContainer>
<ErrorContent title={title} timSays={timSays} />
</Page>
)
}
Expand Down
4 changes: 2 additions & 2 deletions frontends/main/src/app/dashboard/organization/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ const Page: React.FC<PageProps<"/dashboard/organization/[slug]">> = async ({
params,
}) => {
const resolved = await params
invariant(resolved?.slug, "slug is required")
return <OrganizationContent orgSlug={resolved?.slug} />
invariant(resolved.slug, "slug is required")
return <OrganizationContent orgSlug={resolved.slug} />
}

export default Page
8 changes: 8 additions & 0 deletions frontends/main/src/app/dashboard/organization/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from "react"
import OrganizationRedirect from "@/app-pages/DashboardPage/OrganizationRedirect"

const Page: React.FC = async () => {
return <OrganizationRedirect />
}

export default Page
Loading