diff --git a/RELEASE.rst b/RELEASE.rst
index 0e2689d709..71ad1754e4 100644
--- a/RELEASE.rst
+++ b/RELEASE.rst
@@ -1,6 +1,11 @@
Release Notes
=============
+Version 0.55.2
+--------------
+
+- fix: remove the articles route and its relevant code (#2974)
+
Version 0.55.1 (Released February 24, 2026)
--------------
diff --git a/frontends/main/next.config.js b/frontends/main/next.config.js
index 7fd9b59a84..1f0b85bb55 100644
--- a/frontends/main/next.config.js
+++ b/frontends/main/next.config.js
@@ -47,6 +47,18 @@ const nextConfig = {
destination: "/enrollmentcode/:code",
permanent: true,
},
+ {
+ // can be removed once fastly redirect is in place
+ source: "/articles/:slug*",
+ destination: "/news/:slug*",
+ permanent: true,
+ },
+ {
+ // can be removed once fastly redirect is in place
+ source: "/articles",
+ destination: "/news",
+ permanent: true,
+ },
]
},
diff --git a/frontends/main/src/app-pages/Articles/ArticleDraftListingPage.tsx b/frontends/main/src/app-pages/Articles/ArticleDraftListingPage.tsx
index 0f3fff4e1a..5411b021ea 100644
--- a/frontends/main/src/app-pages/Articles/ArticleDraftListingPage.tsx
+++ b/frontends/main/src/app-pages/Articles/ArticleDraftListingPage.tsx
@@ -12,15 +12,15 @@ import {
LoadingSpinner,
Typography,
} from "ol-components"
-import { Permission, useUserHasPermission } from "api/hooks/user"
+import { Permission } from "api/hooks/user"
import { useArticleList } from "api/hooks/articles"
import type { RichTextArticle } from "api/v1"
import { LocalDate } from "ol-utilities"
import { RiArrowLeftLine, RiArrowRightLine } from "@remixicon/react"
import { ArticleBanner, DEFAULT_BACKGROUND_IMAGE_URL } from "./ArticleBanner"
import { extractFirstImageFromArticle } from "@/common/articleUtils"
-import { notFound } from "next/navigation"
import { articlesDraftView, articlesView } from "@/common/urls"
+import RestrictedRoute from "@/components/RestrictedRoute/RestrictedRoute"
const PAGE_SIZE = 20
@@ -109,8 +109,6 @@ const ArticleDraftPage: React.FC = () => {
draft: true, // Filter for drafts only on the backend
})
- const isArticleEditor = useUserHasPermission(Permission.ArticleEditor)
-
useEffect(() => {
if (page > 1 && scrollRef.current) {
scrollRef.current.scrollIntoView({ behavior: "smooth", block: "start" })
@@ -120,11 +118,11 @@ const ArticleDraftPage: React.FC = () => {
const draftArticles = articles?.results
const totalPages = articles?.count ? Math.ceil(articles.count / PAGE_SIZE) : 0
- if (!isLoadingArticles && !isArticleEditor) {
- return notFound()
+ if (isLoadingArticles) {
+ return
}
return (
- <>
+
{
)}
- >
+
)
}
diff --git a/frontends/main/src/app/articles/[slugOrId]/page.tsx b/frontends/main/src/app/articles/[slugOrId]/page.tsx
deleted file mode 100644
index 68ebf8a394..0000000000
--- a/frontends/main/src/app/articles/[slugOrId]/page.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import React from "react"
-import { HydrationBoundary, dehydrate } from "@tanstack/react-query"
-import { articleQueries } from "api/hooks/articles/queries"
-import { ArticleDetailPage } from "@/app-pages/Articles/ArticleDetailPage"
-import { getQueryClient } from "@/app/getQueryClient"
-import { learningResourceQueries } from "api/hooks/learningResources"
-import { extractLearningResourceIds } from "@/page-components/TiptapEditor/extensions/utils"
-import { safeGenerateMetadata, standardizeMetadata } from "@/common/metadata"
-import type { RichTextArticle } from "api/v1"
-import type { JSONContent } from "@tiptap/react"
-
-// Extracts the banner subheading paragraph at known location
-const extractArticleDescription = (
- article: RichTextArticle,
-): string | undefined => {
- const banner = article.content?.content?.[0]
- const subheading = banner?.content?.[1]
- const textNode = subheading?.content?.[0]
- return textNode?.text
-}
-
-const extractImageMetadata = (
- article: RichTextArticle,
-): { src: string; alt: string } | null => {
- const imageWithCaption = article.content?.content?.find(
- (node: JSONContent) => node.type === "imageWithCaption",
- )
- if (!imageWithCaption) {
- return null
- }
- return {
- src: imageWithCaption.attrs.src,
- alt: imageWithCaption.attrs.caption || imageWithCaption.attrs.alt,
- }
-}
-
-export const generateMetadata = async (
- props: PageProps<"/articles/[slugOrId]">,
-) => {
- const params = await props.params
-
- const { slugOrId } = await params
-
- const queryClient = getQueryClient()
-
- return safeGenerateMetadata(async () => {
- const article = await queryClient.fetchQuery(
- articleQueries.articlesDetailRetrieve(slugOrId),
- )
-
- const description = extractArticleDescription(article)
- const leadImage = extractImageMetadata(article)
-
- return standardizeMetadata({
- title: article.title,
- description,
- image: leadImage?.src,
- imageAlt: leadImage?.alt,
- })
- })
-}
-
-const Page: React.FC> = async (props) => {
- const { slugOrId } = await props.params
-
- const queryClient = getQueryClient()
-
- await queryClient.fetchQueryOr404(
- articleQueries.articlesDetailRetrieve(slugOrId),
- )
-
- const queryKey = articleQueries.articlesDetailRetrieve(slugOrId).queryKey
- const cacheData = queryClient.getQueryData(queryKey)
-
- const learningResourceIds = cacheData?.content
- ? extractLearningResourceIds(cacheData.content)
- : []
-
- if (learningResourceIds.length > 0) {
- const bulkQuery = learningResourceQueries.list({
- resource_id: learningResourceIds,
- })
- await queryClient.prefetchQuery(bulkQuery)
- }
-
- return (
-
-
-
- )
-}
-export default Page
diff --git a/frontends/main/src/app/articles/new/page.tsx b/frontends/main/src/app/articles/new/page.tsx
deleted file mode 100644
index 52acf8ab88..0000000000
--- a/frontends/main/src/app/articles/new/page.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from "react"
-import { Metadata } from "next"
-import { standardizeMetadata } from "@/common/metadata"
-import { ArticleNewPage } from "@/app-pages/Articles/ArticleNewPage"
-
-export const metadata: Metadata = standardizeMetadata({
- title: "New Article",
- robots: "noindex, nofollow",
-})
-
-const Page: React.FC> = () => {
- return
-}
-
-export default Page
diff --git a/frontends/main/src/app/articles/[slugOrId]/draft/page.tsx b/frontends/main/src/app/news/[slugOrId]/draft/page.tsx
similarity index 89%
rename from frontends/main/src/app/articles/[slugOrId]/draft/page.tsx
rename to frontends/main/src/app/news/[slugOrId]/draft/page.tsx
index 9967751c83..1f4dc0b464 100644
--- a/frontends/main/src/app/articles/[slugOrId]/draft/page.tsx
+++ b/frontends/main/src/app/news/[slugOrId]/draft/page.tsx
@@ -10,9 +10,7 @@ export const generateMetadata = async () => {
})
}
-const Page: React.FC> = async (
- props,
-) => {
+const Page: React.FC> = async (props) => {
const { slugOrId } = await props.params
// No prefetching for draft articles - the client-side component
diff --git a/frontends/main/src/app/articles/draft/page.tsx b/frontends/main/src/app/news/draft/page.tsx
similarity index 85%
rename from frontends/main/src/app/articles/draft/page.tsx
rename to frontends/main/src/app/news/draft/page.tsx
index a0cd289b6e..56ad03090b 100644
--- a/frontends/main/src/app/articles/draft/page.tsx
+++ b/frontends/main/src/app/news/draft/page.tsx
@@ -8,7 +8,7 @@ export const metadata: Metadata = standardizeMetadata({
robots: "noindex, nofollow",
})
-const Page: React.FC> = () => {
+const Page: React.FC> = () => {
return
}
diff --git a/frontends/main/src/common/urls.ts b/frontends/main/src/common/urls.ts
index 0421332b8f..082564af73 100644
--- a/frontends/main/src/common/urls.ts
+++ b/frontends/main/src/common/urls.ts
@@ -25,7 +25,7 @@ export const programLetterView = (id: string) =>
generatePath(PROGRAMLETTER_VIEW, { id: String(id) })
export const ARTICLES_LISTING = "/news/"
export const ARTICLES_VIEW = "/news/[id]"
-export const ARTICLES_DRAFT_VIEW = "/articles/[id]/draft"
+export const ARTICLES_DRAFT_VIEW = "/news/[id]/draft"
export const ARTICLES_EDIT = "/news/[id]/edit"
export const ARTICLES_CREATE = "/news/new"
export const articlesView = (id: string) =>
diff --git a/frontends/main/src/page-components/TiptapEditor/ArticleEditor.tsx b/frontends/main/src/page-components/TiptapEditor/ArticleEditor.tsx
index 7362fa0852..6c59c6bfb5 100644
--- a/frontends/main/src/page-components/TiptapEditor/ArticleEditor.tsx
+++ b/frontends/main/src/page-components/TiptapEditor/ArticleEditor.tsx
@@ -282,7 +282,7 @@ const ArticleEditor = ({ onSave, readOnly, article }: ArticleEditorProps) => {
Drafts
diff --git a/frontends/main/src/page-components/TiptapEditor/extensions/node/ArticleByLineInfoBar/ArticleByLineInfoBar.tsx b/frontends/main/src/page-components/TiptapEditor/extensions/node/ArticleByLineInfoBar/ArticleByLineInfoBar.tsx
index d7ef06f890..92d32c516e 100644
--- a/frontends/main/src/page-components/TiptapEditor/extensions/node/ArticleByLineInfoBar/ArticleByLineInfoBar.tsx
+++ b/frontends/main/src/page-components/TiptapEditor/extensions/node/ArticleByLineInfoBar/ArticleByLineInfoBar.tsx
@@ -117,7 +117,7 @@ export const ArticleByLineInfoBarContent = ({
title={article?.title ?? ""}
anchorEl={shareButtonRef.current}
onClose={() => setShareOpen(false)}
- pageUrl={`${NEXT_PUBLIC_ORIGIN}/articles/${article?.slug}`}
+ pageUrl={`${NEXT_PUBLIC_ORIGIN}/news/${article?.slug}`}
/>
{(displayAuthorName || isEditable) && (
diff --git a/main/settings.py b/main/settings.py
index e279435ce3..ded787529b 100644
--- a/main/settings.py
+++ b/main/settings.py
@@ -34,7 +34,7 @@
from main.settings_pluggy import * # noqa: F403
from openapi.settings_spectacular import open_spectacular_settings
-VERSION = "0.55.1"
+VERSION = "0.55.2"
log = logging.getLogger()