Fix feedback report formatting by parsing HTML and bullet text
Add formatFeedbackText() utility that converts raw HTML tags and concatenated bullet points from Gemini API into properly formatted React elements with proper line breaks and list styling. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
907c3a520e
commit
17495d4291
1 changed files with 72 additions and 4 deletions
|
|
@ -7,6 +7,72 @@ import { BugIcon } from './icons/BugIcon';
|
|||
import { ExportIcon } from './icons/ExportIcon';
|
||||
import { LegalIcon } from './icons/LegalIcon';
|
||||
|
||||
/**
|
||||
* Formats feedback text from Gemini API into properly structured React elements.
|
||||
* Handles HTML tags, bullet characters, and newline-separated text.
|
||||
*/
|
||||
const formatFeedbackText = (text: string): React.ReactNode => {
|
||||
if (!text) return null;
|
||||
|
||||
// First, handle HTML tags by converting them to a normalized format
|
||||
let normalizedText = text
|
||||
// Replace </li> and </ul> with newlines
|
||||
.replace(/<\/li>/gi, '\n')
|
||||
.replace(/<\/ul>/gi, '\n')
|
||||
// Remove opening tags
|
||||
.replace(/<ul[^>]*>/gi, '')
|
||||
.replace(/<li[^>]*>/gi, '• ')
|
||||
// Remove any other HTML tags
|
||||
.replace(/<[^>]+>/g, '')
|
||||
// Normalize whitespace around bullets
|
||||
.replace(/\s*•\s*/g, '\n• ')
|
||||
// Clean up multiple newlines
|
||||
.replace(/\n{2,}/g, '\n')
|
||||
.trim();
|
||||
|
||||
// Split into lines
|
||||
const lines = normalizedText.split('\n').filter(line => line.trim());
|
||||
|
||||
// Separate intro text from bullet points
|
||||
const bulletLines: string[] = [];
|
||||
const introLines: string[] = [];
|
||||
|
||||
lines.forEach(line => {
|
||||
const trimmed = line.trim();
|
||||
if (trimmed.startsWith('•')) {
|
||||
// Remove the bullet character, we'll add it via CSS
|
||||
bulletLines.push(trimmed.replace(/^•\s*/, '').trim());
|
||||
} else if (trimmed) {
|
||||
// If we haven't started collecting bullets yet, it's intro text
|
||||
if (bulletLines.length === 0) {
|
||||
introLines.push(trimmed);
|
||||
} else {
|
||||
// Text after bullets - treat as continuation of last bullet
|
||||
const lastBullet = bulletLines.pop();
|
||||
if (lastBullet) {
|
||||
bulletLines.push(`${lastBullet} ${trimmed}`);
|
||||
} else {
|
||||
bulletLines.push(trimmed);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{introLines.length > 0 && (
|
||||
<p className="mb-3">{introLines.join(' ')}</p>
|
||||
)}
|
||||
{bulletLines.length > 0 && (
|
||||
<ul className="list-disc list-inside space-y-1">
|
||||
{bulletLines.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const RagStatusBadge: React.FC<{ status: RagStatus; isLarge?: boolean }> = ({ status, isLarge = false }) => {
|
||||
let colorClasses = '';
|
||||
|
|
@ -301,7 +367,9 @@ const SubReviewCard: React.FC<{
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-slate-600 text-sm leading-relaxed mb-6">{review.feedback}</p>
|
||||
<div className="text-slate-600 text-sm leading-relaxed mb-6">
|
||||
{formatFeedbackText(review.feedback)}
|
||||
</div>
|
||||
|
||||
{currentStatus !== 'Error' && issues.length > 0 && (
|
||||
<div className="bg-white/50 rounded-xl border border-slate-100 p-4">
|
||||
|
|
@ -406,9 +474,9 @@ const LeadAgentSummary: React.FC<{ status: OverallStatus, summary: string, onFla
|
|||
<FlagIcon className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
<p className={`text-lg leading-relaxed ${isPassed ? 'text-emerald-800/80' : 'text-slate-700'}`}>
|
||||
{summary}
|
||||
</p>
|
||||
<div className={`text-lg leading-relaxed ${isPassed ? 'text-emerald-800/80' : 'text-slate-700'}`}>
|
||||
{formatFeedbackText(summary)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue