diff --git a/frontends/main/src/app-pages/DashboardPage/OrganizationContent.test.tsx b/frontends/main/src/app-pages/DashboardPage/OrganizationContent.test.tsx
index 4ab71c50db..05924aaeba 100644
--- a/frontends/main/src/app-pages/DashboardPage/OrganizationContent.test.tsx
+++ b/frontends/main/src/app-pages/DashboardPage/OrganizationContent.test.tsx
@@ -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()
+
+ await screen.findByRole("heading", { name: "Organization not found" })
+ })
})
diff --git a/frontends/main/src/app-pages/DashboardPage/OrganizationContent.tsx b/frontends/main/src/app-pages/DashboardPage/OrganizationContent.tsx
index b3d804df5c..60840229ff 100644
--- a/frontends/main/src/app-pages/DashboardPage/OrganizationContent.tsx
+++ b/frontends/main/src/app-pages/DashboardPage/OrganizationContent.tsx
@@ -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"
@@ -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",
@@ -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 = ({
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 = (
-
+
+ useEffect(() => {
+ if (
+ mitxOnlineUser?.b2b_organizations.find(matchOrganizationBySlug(orgSlug))
+ ) {
+ localStorage.setItem("last-dashboard-org", orgSlug)
+ }
+ }, [mitxOnlineUser, orgSlug])
+
+ if (isLoadingMitxOnlineUser) {
+ return (
+
+ )
+ }
+
+ const b2bOrganization = mitxOnlineUser?.b2b_organizations.find(
+ matchOrganizationBySlug(orgSlug),
)
- if (isLoadingMitxOnlineUser || isLoadingMitxOnlineUser) return skeleton
- return b2bOrganization ? (
-
- ) : null
+
+ if (!b2bOrganization) {
+ return
+ }
+
+ return
}
export default OrganizationContent
diff --git a/frontends/main/src/app-pages/DashboardPage/OrganizationRedirect.test.tsx b/frontends/main/src/app-pages/DashboardPage/OrganizationRedirect.test.tsx
new file mode 100644
index 0000000000..2d8547be52
--- /dev/null
+++ b/frontends/main/src/app-pages/DashboardPage/OrganizationRedirect.test.tsx
@@ -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()
+
+ 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()
+ 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()
+
+ await waitFor(() => {
+ expect(mockReplace).toHaveBeenCalledWith("/dashboard")
+ })
+ })
+})
diff --git a/frontends/main/src/app-pages/DashboardPage/OrganizationRedirect.tsx b/frontends/main/src/app-pages/DashboardPage/OrganizationRedirect.tsx
new file mode 100644
index 0000000000..8ac20f5b3a
--- /dev/null
+++ b/frontends/main/src/app-pages/DashboardPage/OrganizationRedirect.tsx
@@ -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 (
+
+ )
+ }
+
+ return null
+}
+
+export default OrganizationRedirect
diff --git a/frontends/main/src/app-pages/ErrorPage/ErrorPageTemplate.tsx b/frontends/main/src/app-pages/ErrorPage/ErrorPageTemplate.tsx
index 173e2aac8d..000e0212ff 100644
--- a/frontends/main/src/app-pages/ErrorPage/ErrorPageTemplate.tsx
+++ b/frontends/main/src/app-pages/ErrorPage/ErrorPageTemplate.tsx
@@ -81,6 +81,33 @@ const Button = styled(ButtonLink)({
minWidth: "200px",
})
+export const ErrorContent: React.FC = ({
+ title,
+ timSays,
+}) => {
+ return (
+
+
+
+ {timSays || "Oops!"}
+
+
+
+ {title}
+
+
+
+ )
+}
+
const ErrorPageTemplate: React.FC = ({
title,
timSays,
@@ -109,25 +136,7 @@ const ErrorPageTemplate: React.FC = ({
}
return (
-
-
-
- {timSays || "Oops!"}
-
-
-
- {title}
-
-
-
+
)
}
diff --git a/frontends/main/src/app/dashboard/organization/[slug]/page.tsx b/frontends/main/src/app/dashboard/organization/[slug]/page.tsx
index 1eb88be809..690c46961b 100644
--- a/frontends/main/src/app/dashboard/organization/[slug]/page.tsx
+++ b/frontends/main/src/app/dashboard/organization/[slug]/page.tsx
@@ -6,8 +6,8 @@ const Page: React.FC> = async ({
params,
}) => {
const resolved = await params
- invariant(resolved?.slug, "slug is required")
- return
+ invariant(resolved.slug, "slug is required")
+ return
}
export default Page
diff --git a/frontends/main/src/app/dashboard/organization/page.tsx b/frontends/main/src/app/dashboard/organization/page.tsx
new file mode 100644
index 0000000000..f825983677
--- /dev/null
+++ b/frontends/main/src/app/dashboard/organization/page.tsx
@@ -0,0 +1,8 @@
+import React from "react"
+import OrganizationRedirect from "@/app-pages/DashboardPage/OrganizationRedirect"
+
+const Page: React.FC = async () => {
+ return
+}
+
+export default Page