diff --git a/RELEASE.rst b/RELEASE.rst index 883ab6b99d..abfdc01d56 100644 --- a/RELEASE.rst +++ b/RELEASE.rst @@ -1,6 +1,15 @@ Release Notes ============= +Version 0.71.3 +-------------- + +- Fix error pages after swap to runtime env vars (#3448) +- add scm as synonym to search (#3449) +- remove duplicate resource task (#3436) +- fix: add link to the creation page of article and news (#3430) +- Remove video shorts app (Phase 2) (#3385) + Version 0.71.1 (Released June 08, 2026) -------------- diff --git a/frontends/main/src/app-pages/Articles/ArticleListingPage.tsx b/frontends/main/src/app-pages/Articles/ArticleListingPage.tsx index df312e0f7b..ddcc08ccde 100644 --- a/frontends/main/src/app-pages/Articles/ArticleListingPage.tsx +++ b/frontends/main/src/app-pages/Articles/ArticleListingPage.tsx @@ -22,7 +22,9 @@ import type { WebsiteContent } from "api/v1" import { LocalDate } from "ol-utilities" import { useWebsiteContentList } from "api/hooks/website_content" import { extractArticleContent } from "@/common/websiteContentUtils" -import { articleView } from "@/common/urls" +import { articleView, websiteContentCreateView } from "@/common/urls" +import { Permission, useUserHasPermission } from "api/hooks/user" +import { ButtonLink } from "@mitodl/smoot-design" const PAGE_SIZE = 10 const MAX_PAGE = 50 @@ -223,6 +225,10 @@ const BannerGridContainer = styled.div` max-width: 842px; margin: 0 auto; padding: 64px 0; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; ${theme.breakpoints.down("sm")} { max-width: 100%; @@ -299,6 +305,10 @@ const BannerSection = styled.div` background: ${theme.custom.colors.white}; border-bottom: 1px solid ${theme.custom.colors.lightGray2}; ` +const NewArticleLink = styled(ButtonLink)` + display: flex; + justify-content: end; +` const EmptyState = styled.div` display: flex; @@ -319,7 +329,6 @@ const EmptyState = styled.div` const BannerTitle = styled(Typography)` color: ${theme.custom.colors.black}; - margin-top: 8px; ${theme.breakpoints.down("md")} { ${{ ...theme.typography.h2 }} } @@ -329,16 +338,6 @@ const BannerTitle = styled(Typography)` } ` as typeof Typography -const BannerDescription = styled(Typography)` - color: ${theme.custom.colors.black}; - margin-top: 16px; - font-size: 20px; - line-height: 32px; - ${theme.breakpoints.down("sm")} { - ${{ ...theme.typography.body2 }} - } -` - const BreadcrumbBar = styled.div(({ theme }) => ({ width: "100%", padding: "18px 0 2px 0", @@ -418,6 +417,8 @@ const ArticleListingPage: React.FC = () => { const parsedPage = Number.parseInt(searchParams.get("page") ?? "1", 10) const page = Number.isFinite(parsedPage) && parsedPage > 0 ? parsedPage : 1 + const isArticleEditor = useUserHasPermission(Permission.ArticleEditor) + const { data: articles, isLoading } = useWebsiteContentList({ limit: PAGE_SIZE, offset: (page - 1) * PAGE_SIZE, @@ -454,11 +455,16 @@ const ArticleListingPage: React.FC = () => { Articles - - Dive into articles covering emerging technologies, global - challenges, and creative thinking. Stay connected with the latest - insights and discoveries from MIT. - + {isArticleEditor && ( + + + New Article + + + )} diff --git a/frontends/main/src/app-pages/News/NewsBanner.tsx b/frontends/main/src/app-pages/News/NewsBanner.tsx index cf3acc519f..422e912143 100644 --- a/frontends/main/src/app-pages/News/NewsBanner.tsx +++ b/frontends/main/src/app-pages/News/NewsBanner.tsx @@ -7,6 +7,9 @@ import { Breadcrumbs, BannerBackground, } from "ol-components" +import { ButtonLink } from "@mitodl/smoot-design" +import { Permission, useUserHasPermission } from "api/hooks/user" +import { websiteContentCreateView } from "@/common/urls" export const DEFAULT_BACKGROUND_IMAGE_URL = "/images/backgrounds/backgroung_steps.jpg" @@ -30,7 +33,17 @@ const BannerSection = styled(BannerBackground)` z-index: 2; } ` - +const NewArticleLink = styled(ButtonLink)` + display: flex; + justify-content: end; +` +const InfoContainer = styled.div` + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + gap: 16px; +` const BannerTitle = styled(Typography)` color: ${theme.custom.colors.white}; margin-top: 8px; @@ -65,6 +78,7 @@ const NewsBanner: React.FC = ({ backgroundUrl = DEFAULT_BACKGROUND_IMAGE_URL, className, }) => { + const isArticleEditor = useUserHasPermission(Permission.ArticleEditor) return ( = ({ ancestors={[{ href: "/", label: "Home" }]} current={currentBreadcrumb} /> - - {title} - - {description} + + + + {title} + + {description} + + {isArticleEditor && ( + + Add news + + )} + ) diff --git a/frontends/main/src/app-pages/News/NewsListingPage.test.tsx b/frontends/main/src/app-pages/News/NewsListingPage.test.tsx index ef1140b62e..400b7ff894 100644 --- a/frontends/main/src/app-pages/News/NewsListingPage.test.tsx +++ b/frontends/main/src/app-pages/News/NewsListingPage.test.tsx @@ -23,6 +23,7 @@ describe("NewsListingPage", () => { beforeEach(() => { mockedUseFeatureFlagEnabled.mockReturnValue(true) mockedUseFeatureFlagsLoaded.mockReturnValue(true) + setMockResponse.get(urls.userMe.get(), { is_authenticated: false }) }) afterEach(() => { @@ -82,6 +83,34 @@ describe("NewsListingPage", () => { }) }) + test("shows 'Add news' button in banner when user is an article editor", async () => { + setMockResponse.get(urls.userMe.get(), { + is_authenticated: true, + is_article_editor: true, + }) + setupAPI(21) + renderWithProviders() + + await waitFor(() => { + expect( + screen.getByRole("link", { name: /add news/i }), + ).toBeInTheDocument() + }) + }) + + test("hides 'Add news' button in banner when user is not an article editor", async () => { + setupAPI(21) + renderWithProviders() + + await waitFor(() => { + expect(screen.queryByRole("progressbar")).not.toBeInTheDocument() + }) + + expect( + screen.queryByRole("link", { name: /add news/i }), + ).not.toBeInTheDocument() + }) + test("displays News images when available", async () => { const news = setupAPI(21) renderWithProviders() diff --git a/frontends/main/src/app/components/PublicEnvScript.tsx b/frontends/main/src/app/components/PublicEnvScript.tsx index 8709bdbfe1..c7139b46e8 100644 --- a/frontends/main/src/app/components/PublicEnvScript.tsx +++ b/frontends/main/src/app/components/PublicEnvScript.tsx @@ -18,19 +18,16 @@ */ import React from "react" import { connection } from "next/server" +import { publicEnvObject } from "@/env" export async function PublicEnvScript() { // `connection()` opts this route out of static prerendering so that // process.env is read fresh on every request (not baked at build time). await connection() - const publicEnv = Object.fromEntries( - Object.entries(process.env).filter(([k]) => k.startsWith("NEXT_PUBLIC_")), - ) - // Escape `<` to prevent a value like `