From 3bee63e2b54410dfe0262d38d9d29d41c106d3d6 Mon Sep 17 00:00:00 2001 From: Danielle Frappier Date: Mon, 11 May 2026 10:47:27 -0400 Subject: [PATCH 01/23] add video embed page and route group restructure --- .../VideoEmbedPage/VideoEmbedPage.test.tsx | 115 ++++++++++++++++++ .../VideoEmbedPage/VideoEmbedPage.tsx | 99 +++++++++++++++ frontends/main/src/app/(embed)/layout.tsx | 15 +++ .../src/app/(embed)/video/[id]/embed/page.tsx | 50 ++++++++ .../(products)/courses/[readable_id]/page.tsx | 0 .../courses/p/[readable_id]/page.tsx | 0 .../programs/[readable_id]/page.tsx | 0 .../main/src/app/{ => (site)}/about/page.tsx | 0 .../c/[channelType]/[name]/page.tsx | 0 .../main/src/app/{ => (site)}/cart/page.tsx | 0 .../[certificateType]/[uuid]/page.tsx | 0 .../[uuid]/pdf/route.test.tsx | 0 .../[certificateType]/[uuid]/pdf/route.tsx | 0 .../[certificateType]/[uuid]/pdf/utils.ts | 0 .../src/app/{ => (site)}/dashboard/layout.tsx | 0 .../dashboard/my-lists/[id]/page.tsx | 0 .../{ => (site)}/dashboard/my-lists/page.tsx | 0 .../contract/[contractSlug]/page.tsx | 0 .../organization/[orgSlug]/page.test.tsx | 0 .../dashboard/organization/[orgSlug]/page.tsx | 0 .../dashboard/organization/page.tsx | 0 .../src/app/{ => (site)}/dashboard/page.tsx | 0 .../{ => (site)}/dashboard/profile/page.tsx | 0 .../dashboard/program/[id]/page.tsx | 0 .../{ => (site)}/dashboard/settings/page.tsx | 0 .../src/app/{ => (site)}/departments/page.tsx | 0 .../enrollmentcode/[code]/page.tsx | 0 .../main/src/app/{ => (site)}/error.test.tsx | 0 frontends/main/src/app/{ => (site)}/error.tsx | 0 .../src/app/{ => (site)}/honor_code/page.tsx | 0 frontends/main/src/app/(site)/layout.tsx | 22 ++++ .../{ => (site)}/learningpaths/[id]/page.tsx | 0 .../app/{ => (site)}/learningpaths/page.tsx | 0 .../news/[slugOrId]/draft/page.tsx | 0 .../news/[slugOrId]/edit/page.tsx | 0 .../app/{ => (site)}/news/[slugOrId]/page.tsx | 0 .../src/app/{ => (site)}/news/draft/page.tsx | 0 .../src/app/{ => (site)}/news/new/page.tsx | 0 .../main/src/app/{ => (site)}/news/page.tsx | 0 .../main/src/app/{ => (site)}/not-found.tsx | 0 .../src/app/{ => (site)}/onboarding/page.tsx | 0 frontends/main/src/app/{ => (site)}/page.tsx | 0 .../{ => (site)}/podcast/[podcastId]/page.tsx | 0 .../podcast_episode/[episodeId]/page.tsx | 0 .../src/app/{ => (site)}/privacy/page.tsx | 0 .../program_letter/[id]/view/page.tsx | 0 .../main/src/app/{ => (site)}/search/page.tsx | 0 .../main/src/app/{ => (site)}/terms/page.tsx | 0 .../main/src/app/{ => (site)}/topics/page.tsx | 0 .../main/src/app/{ => (site)}/units/page.tsx | 0 .../universal-learning/ai/page.tsx | 0 .../{ => (site)}/video-playlist/[id]/page.tsx | 0 .../video-playlist/detail/[id]/page.tsx | 94 ++++++++++++++ frontends/main/src/app/layout.tsx | 13 +- 54 files changed, 396 insertions(+), 12 deletions(-) create mode 100644 frontends/main/src/app-pages/VideoEmbedPage/VideoEmbedPage.test.tsx create mode 100644 frontends/main/src/app-pages/VideoEmbedPage/VideoEmbedPage.tsx create mode 100644 frontends/main/src/app/(embed)/layout.tsx create mode 100644 frontends/main/src/app/(embed)/video/[id]/embed/page.tsx rename frontends/main/src/app/{ => (site)}/(products)/courses/[readable_id]/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/(products)/courses/p/[readable_id]/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/(products)/programs/[readable_id]/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/about/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/c/[channelType]/[name]/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/cart/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/certificate/[certificateType]/[uuid]/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/certificate/[certificateType]/[uuid]/pdf/route.test.tsx (100%) rename frontends/main/src/app/{ => (site)}/certificate/[certificateType]/[uuid]/pdf/route.tsx (100%) rename frontends/main/src/app/{ => (site)}/certificate/[certificateType]/[uuid]/pdf/utils.ts (100%) rename frontends/main/src/app/{ => (site)}/dashboard/layout.tsx (100%) rename frontends/main/src/app/{ => (site)}/dashboard/my-lists/[id]/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/dashboard/my-lists/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/dashboard/organization/[orgSlug]/contract/[contractSlug]/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/dashboard/organization/[orgSlug]/page.test.tsx (100%) rename frontends/main/src/app/{ => (site)}/dashboard/organization/[orgSlug]/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/dashboard/organization/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/dashboard/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/dashboard/profile/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/dashboard/program/[id]/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/dashboard/settings/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/departments/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/enrollmentcode/[code]/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/error.test.tsx (100%) rename frontends/main/src/app/{ => (site)}/error.tsx (100%) rename frontends/main/src/app/{ => (site)}/honor_code/page.tsx (100%) create mode 100644 frontends/main/src/app/(site)/layout.tsx rename frontends/main/src/app/{ => (site)}/learningpaths/[id]/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/learningpaths/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/news/[slugOrId]/draft/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/news/[slugOrId]/edit/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/news/[slugOrId]/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/news/draft/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/news/new/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/news/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/not-found.tsx (100%) rename frontends/main/src/app/{ => (site)}/onboarding/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/podcast/[podcastId]/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/podcast/[podcastId]/podcast_episode/[episodeId]/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/privacy/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/program_letter/[id]/view/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/search/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/terms/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/topics/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/units/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/universal-learning/ai/page.tsx (100%) rename frontends/main/src/app/{ => (site)}/video-playlist/[id]/page.tsx (100%) create mode 100644 frontends/main/src/app/(site)/video-playlist/detail/[id]/page.tsx diff --git a/frontends/main/src/app-pages/VideoEmbedPage/VideoEmbedPage.test.tsx b/frontends/main/src/app-pages/VideoEmbedPage/VideoEmbedPage.test.tsx new file mode 100644 index 0000000000..b67afab300 --- /dev/null +++ b/frontends/main/src/app-pages/VideoEmbedPage/VideoEmbedPage.test.tsx @@ -0,0 +1,115 @@ +import React from "react" +import { setMockResponse, urls, factories } from "api/test-utils" +import { renderWithProviders, screen } from "@/test-utils" +import { notFound } from "next/navigation" +import { useFeatureFlagEnabled } from "posthog-js/react" +import { useFeatureFlagsLoaded } from "@/common/useFeatureFlagsLoaded" +import VideoEmbedPage from "./VideoEmbedPage" +import type { VideoResource } from "api/v1" +import { ResourceTypeEnum } from "api/v1" + +jest.mock("posthog-js/react") +const mockedUseFeatureFlagEnabled = jest.mocked(useFeatureFlagEnabled) + +jest.mock("@/common/useFeatureFlagsLoaded") +const mockedUseFeatureFlagsLoaded = jest.mocked(useFeatureFlagsLoaded) + +jest.mock("next-nprogress-bar", () => ({ + useRouter: () => ({}), +})) + +jest.mock("next/navigation", () => ({ + ...jest.requireActual("next/navigation"), + notFound: jest.fn(), +})) + +jest.mock("@/app-pages/VideoPlaylistCollectionPage/VideoJsPlayer", () => ({ + __esModule: true, + default: (props: { sources?: { src: string; type: string }[] }) => ( +
+ ), +})) + +const makeVideo = (overrides: Partial = {}): VideoResource => + factories.learningResources.video({ + resource_type: ResourceTypeEnum.Video, + video: { + id: 1, + streaming_url: "https://example.com/video.m3u8", + duration: "PT10M", + caption_urls: [], + cover_image_url: null, + }, + ...overrides, + }) as VideoResource + +const setupVideoApi = (video: VideoResource) => { + setMockResponse.get(urls.learningResources.details({ id: video.id }), video) +} + +describe("VideoEmbedPage", () => { + beforeEach(() => { + mockedUseFeatureFlagEnabled.mockReturnValue(true) + mockedUseFeatureFlagsLoaded.mockReturnValue(true) + }) + + test("renders VideoJsPlayer for a video with a streaming URL", async () => { + const video = makeVideo() + setupVideoApi(video) + + renderWithProviders() + + const player = await screen.findByTestId("video-js-player") + expect(player).toBeInTheDocument() + + const sources = JSON.parse(player.getAttribute("data-sources") ?? "[]") + expect(sources[0].type).toBe("application/x-mpegURL") + }) + + test("renders VideoJsPlayer with mp4 source type", async () => { + const video = makeVideo({ + video: { + id: 2, + streaming_url: "https://example.com/video.mp4", + duration: "PT5M", + caption_urls: [], + cover_image_url: null, + }, + }) + setupVideoApi(video) + + renderWithProviders() + + const player = await screen.findByTestId("video-js-player") + const sources = JSON.parse(player.getAttribute("data-sources") ?? "[]") + expect(sources[0].type).toBe("video/mp4") + }) + + test("calls notFound when feature flag is off and flags are loaded", async () => { + mockedUseFeatureFlagEnabled.mockReturnValue(false) + mockedUseFeatureFlagsLoaded.mockReturnValue(true) + + const video = makeVideo() + setupVideoApi(video) + + renderWithProviders() + + await screen.findByTestId("video-js-player").catch(() => null) + expect(notFound).toHaveBeenCalled() + }) + + test("does not call notFound while feature flags are still loading", () => { + mockedUseFeatureFlagEnabled.mockReturnValue(false) + mockedUseFeatureFlagsLoaded.mockReturnValue(false) + + const video = makeVideo() + setupVideoApi(video) + + renderWithProviders() + + expect(notFound).not.toHaveBeenCalled() + }) +}) diff --git a/frontends/main/src/app-pages/VideoEmbedPage/VideoEmbedPage.tsx b/frontends/main/src/app-pages/VideoEmbedPage/VideoEmbedPage.tsx new file mode 100644 index 0000000000..246e3f504b --- /dev/null +++ b/frontends/main/src/app-pages/VideoEmbedPage/VideoEmbedPage.tsx @@ -0,0 +1,99 @@ +"use client" + +import React, { useMemo } from "react" +import dynamic from "next/dynamic" +import { styled } from "ol-components" +import { useFeatureFlagEnabled } from "posthog-js/react" +import { notFound } from "next/navigation" +import { useLearningResourcesDetail } from "api/hooks/learningResources" +import { resolveVideoSources } from "@/app-pages/VideoPlaylistCollectionPage/videoSources" +import { FeatureFlags } from "@/common/feature_flags" +import { useFeatureFlagsLoaded } from "@/common/useFeatureFlagsLoaded" +import type { VideoJsPlayerProps } from "@/app-pages/VideoPlaylistCollectionPage/VideoJsPlayer" +import type { VideoResource } from "api/v1" + +const VideoJsPlayer = dynamic( + () => import("@/app-pages/VideoPlaylistCollectionPage/VideoJsPlayer"), + { ssr: false }, +) + +const EmbedContainer = styled.div({ + position: "relative", + width: "100vw", + height: "100vh", + maxHeight: "100vh", + backgroundColor: "#000", + overflow: "hidden", + ".video-js, .vjs-tech": { + width: "100% !important", + height: "100% !important", + position: "absolute", + top: 0, + left: 0, + }, + ".vjs-big-play-button": { + width: "92px !important", + height: "92px !important", + lineHeight: "92px !important", + borderRadius: "50% !important", + backgroundColor: "#d8daddb3 !important", + border: "none !important", + fontSize: "4em !important", + marginTop: "-1.25em !important", + marginLeft: "-1.18em !important", + }, +}) + +type VideoEmbedPageProps = { + videoId: number +} + +const VideoEmbedPage: React.FC = ({ videoId }) => { + const showVideoPlaylistPage = useFeatureFlagEnabled( + FeatureFlags.VideoPlaylistPage, + ) + const flagsLoaded = useFeatureFlagsLoaded() + + const { data: resource } = useLearningResourcesDetail(videoId) + const video = resource as VideoResource | undefined + + const sources = useMemo( + () => + video + ? resolveVideoSources( + video.video?.streaming_url, + video.url, + video.content_files?.[0]?.youtube_id, + ) + : [], + [video], + ) + + if (!showVideoPlaylistPage && flagsLoaded) { + notFound() + } + + if (!showVideoPlaylistPage) { + return null + } + + return ( + + {sources.length > 0 && ( + + )} + + ) +} + +export default VideoEmbedPage diff --git a/frontends/main/src/app/(embed)/layout.tsx b/frontends/main/src/app/(embed)/layout.tsx new file mode 100644 index 0000000000..27cfa9f248 --- /dev/null +++ b/frontends/main/src/app/(embed)/layout.tsx @@ -0,0 +1,15 @@ +import React from "react" +import { MITLearnGlobalStyles } from "ol-components" + +export default function EmbedLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + <> + + {children} + + ) +} diff --git a/frontends/main/src/app/(embed)/video/[id]/embed/page.tsx b/frontends/main/src/app/(embed)/video/[id]/embed/page.tsx new file mode 100644 index 0000000000..3f449f9dbf --- /dev/null +++ b/frontends/main/src/app/(embed)/video/[id]/embed/page.tsx @@ -0,0 +1,50 @@ +import React from "react" +import { HydrationBoundary, dehydrate } from "@tanstack/react-query" +import { learningResourceQueries } from "api/hooks/learningResources" +import { getQueryClient } from "@/app/getQueryClient" +import { notFound } from "next/navigation" +import { resolveVideoSources } from "@/app-pages/VideoPlaylistCollectionPage/videoSources" +import VideoEmbedPage from "@/app-pages/VideoEmbedPage/VideoEmbedPage" +import { VideoResourceResourceTypeEnum } from "api/v1" +import type { VideoResource } from "api/v1" + +const Page = async ({ + params, +}: { + params: Promise<{ id: string }> +}) => { + const { id } = await params + const videoId = Number(id) + if (!Number.isInteger(videoId) || videoId <= 0) { + notFound() + } + + const queryClient = getQueryClient() + const resource = await queryClient.fetchQueryOr404( + learningResourceQueries.detail(videoId), + ) + + if (resource.resource_type !== VideoResourceResourceTypeEnum.Video) { + notFound() + } + + const videoResource = resource as VideoResource + const sources = resolveVideoSources( + videoResource.video?.streaming_url, + videoResource.url, + videoResource.content_files?.[0]?.youtube_id, + ) + + const SUPPORTED_TYPES = ["application/x-mpegURL", "video/mp4"] + if (sources.length === 0 || !SUPPORTED_TYPES.includes(sources[0].type)) { + notFound() + } + + return ( + + + + ) +} + +export default Page diff --git a/frontends/main/src/app/(products)/courses/[readable_id]/page.tsx b/frontends/main/src/app/(site)/(products)/courses/[readable_id]/page.tsx similarity index 100% rename from frontends/main/src/app/(products)/courses/[readable_id]/page.tsx rename to frontends/main/src/app/(site)/(products)/courses/[readable_id]/page.tsx diff --git a/frontends/main/src/app/(products)/courses/p/[readable_id]/page.tsx b/frontends/main/src/app/(site)/(products)/courses/p/[readable_id]/page.tsx similarity index 100% rename from frontends/main/src/app/(products)/courses/p/[readable_id]/page.tsx rename to frontends/main/src/app/(site)/(products)/courses/p/[readable_id]/page.tsx diff --git a/frontends/main/src/app/(products)/programs/[readable_id]/page.tsx b/frontends/main/src/app/(site)/(products)/programs/[readable_id]/page.tsx similarity index 100% rename from frontends/main/src/app/(products)/programs/[readable_id]/page.tsx rename to frontends/main/src/app/(site)/(products)/programs/[readable_id]/page.tsx diff --git a/frontends/main/src/app/about/page.tsx b/frontends/main/src/app/(site)/about/page.tsx similarity index 100% rename from frontends/main/src/app/about/page.tsx rename to frontends/main/src/app/(site)/about/page.tsx diff --git a/frontends/main/src/app/c/[channelType]/[name]/page.tsx b/frontends/main/src/app/(site)/c/[channelType]/[name]/page.tsx similarity index 100% rename from frontends/main/src/app/c/[channelType]/[name]/page.tsx rename to frontends/main/src/app/(site)/c/[channelType]/[name]/page.tsx diff --git a/frontends/main/src/app/cart/page.tsx b/frontends/main/src/app/(site)/cart/page.tsx similarity index 100% rename from frontends/main/src/app/cart/page.tsx rename to frontends/main/src/app/(site)/cart/page.tsx diff --git a/frontends/main/src/app/certificate/[certificateType]/[uuid]/page.tsx b/frontends/main/src/app/(site)/certificate/[certificateType]/[uuid]/page.tsx similarity index 100% rename from frontends/main/src/app/certificate/[certificateType]/[uuid]/page.tsx rename to frontends/main/src/app/(site)/certificate/[certificateType]/[uuid]/page.tsx diff --git a/frontends/main/src/app/certificate/[certificateType]/[uuid]/pdf/route.test.tsx b/frontends/main/src/app/(site)/certificate/[certificateType]/[uuid]/pdf/route.test.tsx similarity index 100% rename from frontends/main/src/app/certificate/[certificateType]/[uuid]/pdf/route.test.tsx rename to frontends/main/src/app/(site)/certificate/[certificateType]/[uuid]/pdf/route.test.tsx diff --git a/frontends/main/src/app/certificate/[certificateType]/[uuid]/pdf/route.tsx b/frontends/main/src/app/(site)/certificate/[certificateType]/[uuid]/pdf/route.tsx similarity index 100% rename from frontends/main/src/app/certificate/[certificateType]/[uuid]/pdf/route.tsx rename to frontends/main/src/app/(site)/certificate/[certificateType]/[uuid]/pdf/route.tsx diff --git a/frontends/main/src/app/certificate/[certificateType]/[uuid]/pdf/utils.ts b/frontends/main/src/app/(site)/certificate/[certificateType]/[uuid]/pdf/utils.ts similarity index 100% rename from frontends/main/src/app/certificate/[certificateType]/[uuid]/pdf/utils.ts rename to frontends/main/src/app/(site)/certificate/[certificateType]/[uuid]/pdf/utils.ts diff --git a/frontends/main/src/app/dashboard/layout.tsx b/frontends/main/src/app/(site)/dashboard/layout.tsx similarity index 100% rename from frontends/main/src/app/dashboard/layout.tsx rename to frontends/main/src/app/(site)/dashboard/layout.tsx diff --git a/frontends/main/src/app/dashboard/my-lists/[id]/page.tsx b/frontends/main/src/app/(site)/dashboard/my-lists/[id]/page.tsx similarity index 100% rename from frontends/main/src/app/dashboard/my-lists/[id]/page.tsx rename to frontends/main/src/app/(site)/dashboard/my-lists/[id]/page.tsx diff --git a/frontends/main/src/app/dashboard/my-lists/page.tsx b/frontends/main/src/app/(site)/dashboard/my-lists/page.tsx similarity index 100% rename from frontends/main/src/app/dashboard/my-lists/page.tsx rename to frontends/main/src/app/(site)/dashboard/my-lists/page.tsx diff --git a/frontends/main/src/app/dashboard/organization/[orgSlug]/contract/[contractSlug]/page.tsx b/frontends/main/src/app/(site)/dashboard/organization/[orgSlug]/contract/[contractSlug]/page.tsx similarity index 100% rename from frontends/main/src/app/dashboard/organization/[orgSlug]/contract/[contractSlug]/page.tsx rename to frontends/main/src/app/(site)/dashboard/organization/[orgSlug]/contract/[contractSlug]/page.tsx diff --git a/frontends/main/src/app/dashboard/organization/[orgSlug]/page.test.tsx b/frontends/main/src/app/(site)/dashboard/organization/[orgSlug]/page.test.tsx similarity index 100% rename from frontends/main/src/app/dashboard/organization/[orgSlug]/page.test.tsx rename to frontends/main/src/app/(site)/dashboard/organization/[orgSlug]/page.test.tsx diff --git a/frontends/main/src/app/dashboard/organization/[orgSlug]/page.tsx b/frontends/main/src/app/(site)/dashboard/organization/[orgSlug]/page.tsx similarity index 100% rename from frontends/main/src/app/dashboard/organization/[orgSlug]/page.tsx rename to frontends/main/src/app/(site)/dashboard/organization/[orgSlug]/page.tsx diff --git a/frontends/main/src/app/dashboard/organization/page.tsx b/frontends/main/src/app/(site)/dashboard/organization/page.tsx similarity index 100% rename from frontends/main/src/app/dashboard/organization/page.tsx rename to frontends/main/src/app/(site)/dashboard/organization/page.tsx diff --git a/frontends/main/src/app/dashboard/page.tsx b/frontends/main/src/app/(site)/dashboard/page.tsx similarity index 100% rename from frontends/main/src/app/dashboard/page.tsx rename to frontends/main/src/app/(site)/dashboard/page.tsx diff --git a/frontends/main/src/app/dashboard/profile/page.tsx b/frontends/main/src/app/(site)/dashboard/profile/page.tsx similarity index 100% rename from frontends/main/src/app/dashboard/profile/page.tsx rename to frontends/main/src/app/(site)/dashboard/profile/page.tsx diff --git a/frontends/main/src/app/dashboard/program/[id]/page.tsx b/frontends/main/src/app/(site)/dashboard/program/[id]/page.tsx similarity index 100% rename from frontends/main/src/app/dashboard/program/[id]/page.tsx rename to frontends/main/src/app/(site)/dashboard/program/[id]/page.tsx diff --git a/frontends/main/src/app/dashboard/settings/page.tsx b/frontends/main/src/app/(site)/dashboard/settings/page.tsx similarity index 100% rename from frontends/main/src/app/dashboard/settings/page.tsx rename to frontends/main/src/app/(site)/dashboard/settings/page.tsx diff --git a/frontends/main/src/app/departments/page.tsx b/frontends/main/src/app/(site)/departments/page.tsx similarity index 100% rename from frontends/main/src/app/departments/page.tsx rename to frontends/main/src/app/(site)/departments/page.tsx diff --git a/frontends/main/src/app/enrollmentcode/[code]/page.tsx b/frontends/main/src/app/(site)/enrollmentcode/[code]/page.tsx similarity index 100% rename from frontends/main/src/app/enrollmentcode/[code]/page.tsx rename to frontends/main/src/app/(site)/enrollmentcode/[code]/page.tsx diff --git a/frontends/main/src/app/error.test.tsx b/frontends/main/src/app/(site)/error.test.tsx similarity index 100% rename from frontends/main/src/app/error.test.tsx rename to frontends/main/src/app/(site)/error.test.tsx diff --git a/frontends/main/src/app/error.tsx b/frontends/main/src/app/(site)/error.tsx similarity index 100% rename from frontends/main/src/app/error.tsx rename to frontends/main/src/app/(site)/error.tsx diff --git a/frontends/main/src/app/honor_code/page.tsx b/frontends/main/src/app/(site)/honor_code/page.tsx similarity index 100% rename from frontends/main/src/app/honor_code/page.tsx rename to frontends/main/src/app/(site)/honor_code/page.tsx diff --git a/frontends/main/src/app/(site)/layout.tsx b/frontends/main/src/app/(site)/layout.tsx new file mode 100644 index 0000000000..3d11cbf5ea --- /dev/null +++ b/frontends/main/src/app/(site)/layout.tsx @@ -0,0 +1,22 @@ +import React from "react" +import Header from "@/page-components/Header/Header" +import Footer from "@/page-components/Footer/Footer" +import { PageWrapper, PageWrapperInner } from "@/app/styled" +import { MITLearnGlobalStyles } from "ol-components" + +export default function SiteLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + <> + + +
+ {children} +