From 324f84a544a2c47a1bcde54308aff3bd2ad9717c Mon Sep 17 00:00:00 2001 From: Cory Pride Date: Mon, 1 Jun 2026 21:48:21 -0500 Subject: [PATCH 1/2] fix: knowledge center character escaping --- .../components/dashboard/TopContentList.tsx | 11 ++-- frontend/src/lib/decodeHtmlEntities.ts | 6 ++ frontend/src/loaders/routeLoaders.ts | 3 +- .../KnowledgeCenterManagement.tsx | 58 +++++++++++-------- .../pages/knowledge-center/LibraryViewer.tsx | 7 ++- .../ResidentKnowledgeCenter.tsx | 34 ++++++----- .../pages/knowledge-center/VideoViewer.tsx | 17 ++++-- frontend/src/pages/student/ResidentHome.tsx | 28 +++++---- 8 files changed, 100 insertions(+), 64 deletions(-) create mode 100644 frontend/src/lib/decodeHtmlEntities.ts diff --git a/frontend/src/components/dashboard/TopContentList.tsx b/frontend/src/components/dashboard/TopContentList.tsx index 905799123..b1052905e 100644 --- a/frontend/src/components/dashboard/TopContentList.tsx +++ b/frontend/src/components/dashboard/TopContentList.tsx @@ -1,6 +1,7 @@ import { OpenContentItem } from '@/types'; import { ExternalLink } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; +import { decodeHtmlEntities } from '@/lib/decodeHtmlEntities'; interface TopContentListProps { heading: string; @@ -10,6 +11,8 @@ interface TopContentListProps { function ContentRow({ item }: { item: OpenContentItem }) { const navigate = useNavigate(); + const title = decodeHtmlEntities(item.title); + const providerName = decodeHtmlEntities(item.provider_name ?? ''); const handleClick = () => { if (item.content_type === 'video') { @@ -29,7 +32,7 @@ function ContentRow({ item }: { item: OpenContentItem }) { {item.thumbnail_url ? ( {item.title} ) : ( @@ -37,11 +40,11 @@ function ContentRow({ item }: { item: OpenContentItem }) { )}

- {item.title} + {title}

- {item.provider_name && ( + {providerName && (

- {item.provider_name} + {providerName}

)}
diff --git a/frontend/src/lib/decodeHtmlEntities.ts b/frontend/src/lib/decodeHtmlEntities.ts new file mode 100644 index 000000000..81baf785e --- /dev/null +++ b/frontend/src/lib/decodeHtmlEntities.ts @@ -0,0 +1,6 @@ +export function decodeHtmlEntities(input: string): string { + if (!input) return input; + const el = document.createElement('textarea'); + el.innerHTML = input; + return el.value; +} diff --git a/frontend/src/loaders/routeLoaders.ts b/frontend/src/loaders/routeLoaders.ts index 8c79d5d56..dda33eecf 100644 --- a/frontend/src/loaders/routeLoaders.ts +++ b/frontend/src/loaders/routeLoaders.ts @@ -20,6 +20,7 @@ import { } from '@/types'; import API from '@/api/api'; import { fetchUser } from '@/auth/useAuth'; +import { decodeHtmlEntities } from '@/lib/decodeHtmlEntities'; function buildClassBreadcrumbs( cls: Class, @@ -135,7 +136,7 @@ const getLibraryOptionsHelper = async ({ request }: { request: Request }) => { (library) => ({ key: library.id, - value: library.title + value: decodeHtmlEntities(library.title) }) as Option ) : []; diff --git a/frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx b/frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx index 65257e019..faf516f41 100644 --- a/frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx +++ b/frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx @@ -44,6 +44,7 @@ import { formatVideoDuration } from '@/lib/formatters'; import API from '@/api/api'; +import { decodeHtmlEntities } from '@/lib/decodeHtmlEntities'; interface CardHandlers { onToggleFeatured: ( @@ -66,9 +67,11 @@ function LibraryCard({ library: Library; handlers: CardHandlers; }) { + const title = decodeHtmlEntities(library.title); + const description = decodeHtmlEntities(library.description ?? ''); return (
handlers.onNavigate(`/viewer/libraries/${library.id}`) } @@ -102,18 +105,18 @@ function LibraryCard({
{library.title}
-

{library.title}

+

{title}

-

- {library.description} +

+ {description}

e.stopPropagation()} >