Skip to content

Knowledge Center formatting issues#1147

Merged
corypride merged 2 commits into
mainfrom
cpride/kc_formatting
Jun 8, 2026
Merged

Knowledge Center formatting issues#1147
corypride merged 2 commits into
mainfrom
cpride/kc_formatting

Conversation

@corypride

@corypride corypride commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

fix: decode HTML entities in Knowledge Center + card formatting (ID-661)

Pre-Submission PR Checklist

  • No debug/console/fmt.Println statements
  • Unnecessary development comments removed
  • All acceptance criteria verified
  • Functions according to ticket specifications
  • Tested manually where applicable
  • Branch rebased with latest main
  • No business logic exists within the database layer

Description of the change

Fixes two formatting issues in the Knowledge Center reported in ID-661:

  1. HTML entities rendering literally — provider-sourced text (e.g. a library
    titled Cooking Q&A) was displaying the raw entity instead of Q&A.
    Added a small decodeHtmlEntities helper (frontend/src/lib/decodeHtmlEntities.ts)
    and applied it at every live render site that displays provider title /
    description / channel / author / provider-name text, including alt
    attributes (React does not entity-decode JS strings, so alt={title} showed
    the literal & too). The helper is entity-agnostic — it decodes any named
    or numeric HTML entity, not just &.

    Sites updated:

    • KnowledgeCenterManagement.tsx — Library/Video/Link admin cards
    • ResidentKnowledgeCenter.tsx — resident content cards (covers search results)
    • LibraryViewer.tsx / VideoViewer.tsx — viewer headers, descriptions, channel
    • components/dashboard/TopContentList.tsx — dashboard "Top Content" widget
    • pages/student/ResidentHome.tsx — featured libraries, helpful links, favorites
    • loaders/routeLoaders.ts — library filter dropdown option labels
  2. Card styling — cards in a row had uneven heights and the Visible toggle
    row didn't align. Cards now use flex flex-col h-full with a flex-1
    description and mt-auto on the control row, giving equal-height cards with
    bottom-aligned toggles.

Also cleaned up Tailwind v4 canonical-class lint hints on the touched files
(flex-shrink-0shrink-0; arbitrary w-[…px]/h-[…px]/min-h-[2.5rem]
scale equivalents like w-45, w-70, w-80, h-100, h-150, min-h-10).
These are pixel-equivalent — no visual change.

  • Related issues: Asana ID-661

Screenshot(s)

TODO: add before/after of the Knowledge Center cards (1366 x 768 for the
resident-facing views) showing Q&A rendering correctly and aligned toggles.

Additional context

  • Decoding is applied at the render boundary (decode-once-into-const), matching
    the pattern already established on the management cards.
  • Unused pre-redesign components in components/knowledge-center/
    (LibraryCard, VideoCard, HelpfulLinkCard, FavoriteCard,
    OpenContentItemAccordion) were intentionally left untouched — a usage grep
    shows zero imports; they're dead code and out of scope here.
  • tsc --noEmit and eslint pass clean on all edited files.

@corypride corypride requested a review from a team as a code owner June 2, 2026 02:51
@corypride corypride requested review from CK-7vn and removed request for a team June 2, 2026 02:51
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 59df5382-3747-410e-8812-82eb75a215de

📥 Commits

Reviewing files that changed from the base of the PR and between 7aecd79 and 253d128.

📒 Files selected for processing (8)
  • frontend/src/components/dashboard/TopContentList.tsx
  • frontend/src/lib/decodeHtmlEntities.ts
  • frontend/src/loaders/routeLoaders.ts
  • frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx
  • frontend/src/pages/knowledge-center/LibraryViewer.tsx
  • frontend/src/pages/knowledge-center/ResidentKnowledgeCenter.tsx
  • frontend/src/pages/knowledge-center/VideoViewer.tsx
  • frontend/src/pages/student/ResidentHome.tsx

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Fixed HTML entity encoding so titles, descriptions, and provider names render correctly (no more visible entities like   or ") across dashboard, knowledge center, viewers, and home.
  • UI Improvements

    • Adjusted card layouts, spacing, and filter widths for more consistent visual flow.
  • Accessibility

    • Improved image alt text and label rendering so thumbnails and headings use proper, human-readable text.

Walkthrough

Adds a decodeHtmlEntities utility and applies it to titles, descriptions, and author/channel/provider fields across loaders, viewers, dashboard lists, and card components; also tweaks card flex layouts and several Tailwind sizing classes.

Changes

HTML Entity Decoding Across Knowledge Center and Dashboard Pages

Layer / File(s) Summary
HTML entity decoding utility
frontend/src/lib/decodeHtmlEntities.ts
New exported decodeHtmlEntities(input: string) parses HTML entities using a temporary textarea and returns decoded text or the original falsy input.
Route loaders and library data transformation
frontend/src/loaders/routeLoaders.ts
Import decodeHtmlEntities and apply it to library titles when building select option values.
Content viewer pages: Library and Video
frontend/src/pages/knowledge-center/LibraryViewer.tsx, frontend/src/pages/knowledge-center/VideoViewer.tsx
Decode loaded library/video titles, descriptions, and channel names for breadcrumbs, headers, and body text; update loading skeleton heights to Tailwind utilities.
Dashboard content list component
frontend/src/components/dashboard/TopContentList.tsx
Decode item titles and provider names before rendering and for image alt attributes.
Knowledge Center management card components
frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx
LibraryCard, VideoCard, and LinkCard decode displayed text, use decoded titles for alt attributes, adopt flex-column full-height layouts with flex-1 descriptions and mt-auto bottom rows; SelectTrigger widths updated.
Knowledge Center resident view ContentCard
frontend/src/pages/knowledge-center/ResidentKnowledgeCenter.tsx
ContentCard decodes title, description, and author; updates image alt, layout spacing, description min-heights, and category row styling; category filter width changed to w-70.
Student home page card components
frontend/src/pages/student/ResidentHome.tsx
FeaturedLibraryCard, HelpfulLinkCard, and FavoriteItem decode titles/descriptions for displayed text and image alt attributes; favorites sidebar width changed to w-80.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title "Knowledge Center formatting issues" is directly related to the changeset, which fixes two specific formatting issues: HTML entity decoding and card styling alignment in the Knowledge Center.
Description check ✅ Passed The description is well-related to the changeset, providing detailed context about the two formatting fixes, affected files, implementation approach, and testing status.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/lib/decodeHtmlEntities.ts`:
- Around line 1-6: The static analyzer flags the innerHTML assignment in
decodeHtmlEntities as a potential XSS false positive; to resolve this either (A)
replace the textarea trick with DOMParser: in decodeHtmlEntities keep the early
return for falsy input, create a DOMParser, call parseFromString(input,
'text/html') and return document.body.textContent (or textContent of the parsed
document) instead of using el.innerHTML/el.value, or (B) switch to a proven
library like `he`/`html-entities` and call its decode function while preserving
the same falsy-input behavior; alternatively add a short code comment above
decodeHtmlEntities explaining why the textarea approach is safe (detached
element, returning .value) to silence the warning if you want to keep the
current implementation.

In `@frontend/src/pages/knowledge-center/VideoViewer.tsx`:
- Around line 131-133: The JSX accesses video.duration directly which can be
undefined; update the rendering in VideoViewer.tsx to guard against a missing
video by using optional chaining or a null check (e.g., refer to the video
variable and replace video.duration with video?.duration or wrap the whole
expression in a conditional that checks video), ensuring formatVideoDuration is
only called when video is defined (e.g., call
formatVideoDuration(video.duration) only after confirming video exists).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 72d548cb-723c-4c39-9169-91266008615f

📥 Commits

Reviewing files that changed from the base of the PR and between 7d7ecde and 265c48c.

📒 Files selected for processing (8)
  • frontend/src/components/dashboard/TopContentList.tsx
  • frontend/src/lib/decodeHtmlEntities.ts
  • frontend/src/loaders/routeLoaders.ts
  • frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx
  • frontend/src/pages/knowledge-center/LibraryViewer.tsx
  • frontend/src/pages/knowledge-center/ResidentKnowledgeCenter.tsx
  • frontend/src/pages/knowledge-center/VideoViewer.tsx
  • frontend/src/pages/student/ResidentHome.tsx

Comment on lines +1 to +6
export function decodeHtmlEntities(input: string): string {
if (!input) return input;
const el = document.createElement('textarea');
el.innerHTML = input;
return el.value;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Static analysis warnings are false positives for this entity-decoding pattern.

The static analysis tools flagged innerHTML assignment as an XSS risk. However, this specific pattern is safe because:

  1. The textarea element is created in memory and never appended to the DOM
  2. Scripts cannot execute in a detached element
  3. The function returns .value (decoded text), not .innerHTML (HTML content)
  4. This is a standard lightweight technique for HTML entity decoding

That said, if you want to address the warnings explicitly or improve code clarity, consider these alternatives:

Alternative approaches

Option 1: Use DOMParser (more explicit intent, still native)

 export function decodeHtmlEntities(input: string): string {
     if (!input) return input;
-    const el = document.createElement('textarea');
-    el.innerHTML = input;
-    return el.value;
+    const doc = new DOMParser().parseFromString(input, 'text/html');
+    return doc.documentElement.textContent || input;
 }

Option 2: Use a dedicated library like he or html-entities

import { decode } from 'he';

export function decodeHtmlEntities(input: string): string {
    if (!input) return input;
    return decode(input);
}

Libraries handle edge cases and numeric entities more robustly, but add a dependency.

The current implementation is correct and safe for the stated use case (decoding provider-sourced content like "Q&A" → "Q&A").

🧰 Tools
🪛 ast-grep (0.43.0)

[warning] 3-3: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: el.innerHTML = input
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)


[warning] 3-3: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: el.innerHTML = input
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)

🪛 OpenGrep (1.22.0)

[WARNING] 4-4: Setting innerHTML with dynamic content can lead to XSS. Use textContent or createElement with proper escaping instead.

(coderabbit.xss.innerhtml-assignment)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/lib/decodeHtmlEntities.ts` around lines 1 - 6, The static
analyzer flags the innerHTML assignment in decodeHtmlEntities as a potential XSS
false positive; to resolve this either (A) replace the textarea trick with
DOMParser: in decodeHtmlEntities keep the early return for falsy input, create a
DOMParser, call parseFromString(input, 'text/html') and return
document.body.textContent (or textContent of the parsed document) instead of
using el.innerHTML/el.value, or (B) switch to a proven library like
`he`/`html-entities` and call its decode function while preserving the same
falsy-input behavior; alternatively add a short code comment above
decodeHtmlEntities explaining why the textarea approach is safe (detached
element, returning .value) to silence the warning if you want to keep the
current implementation.

Comment thread frontend/src/pages/knowledge-center/VideoViewer.tsx Outdated
@carddev81 carddev81 requested review from carddev81 and removed request for CK-7vn June 4, 2026 15:14

@carddev81 carddev81 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests good and looks good. I did find other issues that I made notes of. I'm creating other tickets for these.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx`:
- Around line 553-562: The toast messages use the same actionString for both
success and failure, causing error branches to display misleading success-like
text; update the error branch to use a failure message (e.g., "failed to
[feature/unfeature]") instead of actionString and include any error info from
resp (or resp.error) when available; locate the block using actionString,
isFeatured, API.put, resp, toast.success/toast.error and change the toast.error
call to display a clear failure message referencing typeLabel[type] and the
inverse verb (e.g., `failed to ${actionString}`) and preferably append
resp.error or resp.message for debugging, and apply the same change to the
similar block that calls mutateLibs/mutateVids/mutateLinks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e107a713-523b-4aac-a6c9-9c583432bb66

📥 Commits

Reviewing files that changed from the base of the PR and between 265c48c and 7aecd79.

📒 Files selected for processing (3)
  • frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx
  • frontend/src/pages/knowledge-center/LibraryViewer.tsx
  • frontend/src/pages/knowledge-center/VideoViewer.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx`:
- Around line 553-562: The toast messages use the same actionString for both
success and failure, causing error branches to display misleading success-like
text; update the error branch to use a failure message (e.g., "failed to
[feature/unfeature]") instead of actionString and include any error info from
resp (or resp.error) when available; locate the block using actionString,
isFeatured, API.put, resp, toast.success/toast.error and change the toast.error
call to display a clear failure message referencing typeLabel[type] and the
inverse verb (e.g., `failed to ${actionString}`) and preferably append
resp.error or resp.message for debugging, and apply the same change to the
similar block that calls mutateLibs/mutateVids/mutateLinks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e107a713-523b-4aac-a6c9-9c583432bb66

📥 Commits

Reviewing files that changed from the base of the PR and between 265c48c and 7aecd79.

📒 Files selected for processing (3)
  • frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx
  • frontend/src/pages/knowledge-center/LibraryViewer.tsx
  • frontend/src/pages/knowledge-center/VideoViewer.tsx
🛑 Comments failed to post (1)
frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx (1)

553-562: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Error toasts currently report success-like outcomes.

On Line 561 and Line 589, failed API calls still render success-style phrases derived from actionString, which can tell admins the update happened when it did not.

Proposed fix
         const actionString = isFeatured ? 'unfeatured' : 'featured';
         const resp = await API.put<null, object>(endpoints[type], {});
         if (resp.success) {
             toast.success(`${typeLabel[type]} ${actionString}`);
             if (type === 'library') void mutateLibs();
             else if (type === 'video') void mutateVids();
             else void mutateLinks();
         } else {
-            toast.error(`${typeLabel[type]} ${actionString}`);
+            toast.error(
+                resp.message ??
+                    `Failed to update featured status for ${typeLabel[type].toLowerCase()}`
+            );
         }
@@
         const actionString = isVisible ? 'is now hidden' : 'is now visible';
         const resp = await API.put<null, object>(endpoints[type], {});
         if (resp.success) {
             toast.success(`${typeLabel[type]} ${actionString}`);
             if (type === 'library') void mutateLibs();
             else if (type === 'video') void mutateVids();
             else void mutateLinks();
         } else {
-            toast.error(`${typeLabel[type]} ${actionString}`);
+            toast.error(
+                resp.message ??
+                    `Failed to update visibility for ${typeLabel[type].toLowerCase()}`
+            );
         }

Also applies to: 581-590

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/knowledge-center/KnowledgeCenterManagement.tsx` around
lines 553 - 562, The toast messages use the same actionString for both success
and failure, causing error branches to display misleading success-like text;
update the error branch to use a failure message (e.g., "failed to
[feature/unfeature]") instead of actionString and include any error info from
resp (or resp.error) when available; locate the block using actionString,
isFeatured, API.put, resp, toast.success/toast.error and change the toast.error
call to display a clear failure message referencing typeLabel[type] and the
inverse verb (e.g., `failed to ${actionString}`) and preferably append
resp.error or resp.message for debugging, and apply the same change to the
similar block that calls mutateLibs/mutateVids/mutateLinks.

@corypride corypride force-pushed the cpride/kc_formatting branch from 7aecd79 to 253d128 Compare June 5, 2026 00:09
@corypride corypride merged commit aa02010 into main Jun 8, 2026
10 checks passed
@corypride corypride deleted the cpride/kc_formatting branch June 8, 2026 17:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants