feat(F-077): completed feature
This commit is contained in:
64
work/artifacts/F-077/architect.md
Normal file
64
work/artifacts/F-077/architect.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# F-077 — Architect: Product short description renders HTML in admin product list
|
||||
|
||||
## Problem
|
||||
|
||||
The admin product listing (`/products`) renders `p.description` as plain text using `{p.description?.slice(0, 60)}`. If the description contains HTML tags (e.g., from a rich text editor), they are displayed as raw text (e.g., `<strong>Fresh</strong>`) instead of being rendered as formatted HTML.
|
||||
|
||||
## Goal
|
||||
|
||||
Render HTML in the brief description field safely (without XSS risk).
|
||||
|
||||
## Scope IN
|
||||
|
||||
- `project/apps/admin/src/app/(dashboard)/products/page.tsx`:
|
||||
- Add a `renderHtml` helper function that strips dangerous tags (script, onclick, onerror, etc.) while preserving safe formatting tags (b, i, em, strong, br, p, etc.)
|
||||
- Replace `{p.description?.slice(0, 60)}` with `renderHtml(p.description ?? '').slice(0, 60)` using `dangerouslySetInnerHTML`
|
||||
- Truncate the sanitized HTML string to 60 chars (keeping HTML tags if within the limit)
|
||||
|
||||
## Scope OUT
|
||||
|
||||
- No new dependencies (simple regex-based sanitization)
|
||||
- No changes to the backend or database
|
||||
- No changes to other pages
|
||||
|
||||
## Design
|
||||
|
||||
```tsx
|
||||
/** Renders HTML safely: strips dangerous tags (script, onclick, onerror, etc.)
|
||||
* while preserving safe formatting tags (b, i, em, strong, br, p, etc.) */
|
||||
function renderHtml(html: string): string {
|
||||
if (!html) return '';
|
||||
return html
|
||||
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
|
||||
.replace(/\son\w+="[^"]*"/gi, '')
|
||||
.replace(/\son\w+='[^']*'/gi, '')
|
||||
.replace(/javascript:/gi, '')
|
||||
.replace(/<iframe/gi, '<iframe')
|
||||
.replace(/<object/gi, '<object')
|
||||
.replace(/<embed/gi, '<embed');
|
||||
}
|
||||
```
|
||||
|
||||
In the product row:
|
||||
```tsx
|
||||
<p
|
||||
className="text-xs text-gray-400 truncate max-w-xs"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: renderHtml(p.description ?? '').slice(0, 60),
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
This renders `<strong>Fresh</strong>` as **Fresh** (bold) instead of literal text.
|
||||
|
||||
## Risk
|
||||
|
||||
- **Low risk**: purely frontend rendering change, no backend or DB changes
|
||||
- Sanitization removes dangerous tags but allows safe formatting HTML
|
||||
|
||||
## Verification
|
||||
|
||||
- `npx tsc --noEmit` admin
|
||||
- `npx eslint` on changed file
|
||||
- `./scripts/verify.sh` green
|
||||
- Manual: check a product with HTML in description renders formatted text in product listing
|
||||
Reference in New Issue
Block a user