import React, { useState, useEffect } from 'react'; import type { AgentReview, SubReview, RagStatus, OverallStatus, FlaggedItem, ResolvedItem } from '../types'; import { CheckCircleIcon, ExclamationTriangleIcon, InformationCircleIcon } from './icons/StatusIcons'; import { FlagIcon } from './icons/FlagIcon'; import { XIcon } from './icons/XIcon'; 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. */ /** * Renders inline markdown bold (**text**) as elements. * Returns an array of React nodes with bold segments wrapped in . */ const renderBoldMarkdown = (text: string): React.ReactNode[] => { const parts: React.ReactNode[] = []; const regex = /\*\*(.+?)\*\*/g; let lastIndex = 0; let match: RegExpExecArray | null; while ((match = regex.exec(text)) !== null) { if (match.index > lastIndex) { parts.push(text.slice(lastIndex, match.index)); } parts.push({match[1]}); lastIndex = regex.lastIndex; } if (lastIndex < text.length) { parts.push(text.slice(lastIndex)); } return parts; }; const formatFeedbackText = (text: string): React.ReactNode => { if (!text) return null; // First, handle HTML tags by converting them to a normalized format let normalizedText = text // Convert literal \n sequences to real newlines .replace(/\\n/g, '\n') // Replace and with newlines .replace(/<\/li>/gi, '\n') .replace(/<\/ul>/gi, '\n') // Remove opening tags .replace(/]*>/gi, '') .replace(/]*>/gi, '• ') // Remove any other HTML tags (but preserve ** markdown bold) .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 // Each bullet is an array of lines (to support multi-line Issue/Recommendation) const bulletGroups: string[][] = []; const introLines: string[] = []; lines.forEach(line => { const trimmed = line.trim(); if (trimmed.startsWith('•')) { // Start a new bullet group bulletGroups.push([trimmed.replace(/^•\s*/, '').trim()]); } else if (trimmed) { if (bulletGroups.length === 0) { // Haven't started bullets yet — it's intro text introLines.push(trimmed); } else { // Continuation line within the current bullet bulletGroups[bulletGroups.length - 1].push(trimmed); } } }); return ( <> {introLines.length > 0 && (

{renderBoldMarkdown(introLines.join(' '))}

)} {bulletGroups.length > 0 && (
    {bulletGroups.map((group, index) => ( {group.map((line, lineIdx) => (
  • 0 ? 'mt-3' : ''}> {renderBoldMarkdown(line)}
  • ))}
    ))}
)} ); }; const RagStatusBadge: React.FC<{ status: RagStatus; isLarge?: boolean }> = ({ status, isLarge = false }) => { let colorClasses = ''; let iconColor = ''; switch (status) { case 'Red': colorClasses = 'bg-error-light border-error text-error'; iconColor = 'text-error'; break; case 'Amber': colorClasses = 'bg-warning-light border-warning text-oliver-black'; iconColor = 'text-warning'; break; case 'Green': colorClasses = 'bg-success-light border-success text-success'; iconColor = 'text-success'; break; case 'Error': colorClasses = 'bg-oliver-grey border-grey-300 text-oliver-black'; iconColor = 'text-oliver-black/60'; break; } const sizeClasses = isLarge ? 'px-4 py-1.5 text-sm rounded-[10px] border shadow-sm' : 'px-2.5 py-1 text-xs rounded-[10px] border shadow-sm'; return (
{status === 'Red' && } {status === 'Amber' && } {status === 'Green' && } {status === 'Error' && } {status}
); }; const ResolveIssueModal: React.FC<{ isOpen: boolean; onClose: () => void; onSubmit: (reason: string) => void; issueText: string; }> = ({ isOpen, onClose, onSubmit, issueText }) => { if (!isOpen) return null; const [reason, setReason] = useState(''); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (reason.trim()) { onSubmit(reason); } }; return (
e.stopPropagation()} >

Resolve Issue

Please provide a reason for manually resolving this issue.

"{issueText}"