import React, { useState, useEffect } from 'react'; import type { AgentReview, SubReview, RagStatus, OverallStatus } 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. */ 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 and with newlines .replace(/<\/li>/gi, '\n') .replace(/<\/ul>/gi, '\n') // Remove opening tags .replace(/]*>/gi, '') .replace(/]*>/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 && (

{introLines.join(' ')}

)} {bulletLines.length > 0 && ( )} ); }; 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-black-title'; iconColor = 'text-warning'; break; case 'Green': colorClasses = 'bg-success-light border-success text-success'; iconColor = 'text-success'; break; case 'Error': colorClasses = 'bg-grey-100 border-grey-300 text-grey-900'; iconColor = 'text-grey-700'; 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}"