File size: 5,398 Bytes
f500247 82d9c42 f500247 82d9c42 f500247 82d9c42 f500247 82d9c42 f500247 82d9c42 f500247 82d9c42 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
import { marked } from "marked";
import DOMPurify from "dompurify";
import { useEffect, useState } from "react";
import BotIcon from "./icons/BotIcon";
import UserIcon from "./icons/UserIcon";
import "./Chat.css";
function render(text) {
return DOMPurify.sanitize(marked.parse(text));
}
// مكون فرعي لكل رسالة للتحكم في التقييم والاقتراحات
function MessageItem({ msg }) {
const [feedback, setFeedback] = useState(null); // 'up' or 'down'
const [showOptions, setShowOptions] = useState(false);
const medicalOptions = [
"Not medically accurate",
"Does not follow ABCDE protocol",
"Too long for emergency context",
"Incorrect dosage suggestion",
"Other medical reason"
];
const handleOptionClick = (option) => {
alert(`Thank you for your feedback: "${option}". This helps improve the ER Assistant.`);
setShowOptions(false);
setFeedback('down');
};
return (
<div className="flex items-start space-x-4 mb-4 relative">
{msg.role === "assistant" ? (
<>
<BotIcon className="h-6 w-6 min-h-6 min-w-6 my-3 text-gray-500 dark:text-gray-300" />
<div className="flex flex-col items-start w-full">
<div className="bg-gray-200 dark:bg-gray-700 rounded-lg p-4 w-full">
<p className="min-h-6 text-gray-800 dark:text-gray-200 overflow-wrap-anywhere">
{msg.content.length > 0 ? (
<span
className="markdown"
dangerouslySetInnerHTML={{ __html: render(msg.content) }}
/>
) : (
<span className="h-6 flex items-center gap-1">
<span className="w-2.5 h-2.5 bg-gray-600 dark:bg-gray-300 rounded-full animate-pulse"></span>
<span className="w-2.5 h-2.5 bg-gray-600 dark:bg-gray-300 rounded-full animate-pulse animation-delay-200"></span>
<span className="w-2.5 h-2.5 bg-gray-600 dark:bg-gray-300 rounded-full animate-pulse animation-delay-400"></span>
</span>
)}
</p>
</div>
{/* أزرار التقييم - تظهر فقط بعد انتهاء الكتابة */}
{msg.content.length > 0 && (
<div className="flex items-center mt-2 space-x-3 text-gray-400 ml-1">
<button
onClick={() => {setFeedback('up'); setShowOptions(false);}}
className={`hover:text-green-500 transition-colors ${feedback === 'up' ? 'text-green-500' : ''}`}
title="Correct"
>
👍
</button>
<button
onClick={() => setShowOptions(!showOptions)}
className={`hover:text-red-500 transition-colors ${feedback === 'down' ? 'text-red-500' : ''}`}
title="Incorrect"
>
👎
</button>
{/* نافذة الأسباب (Popup) */}
{showOptions && (
<div className="absolute z-50 mt-10 left-10 p-4 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-xl shadow-2xl w-64 animate-in fade-in zoom-in duration-200">
<div className="flex justify-between items-center mb-3">
<h4 className="text-xs font-bold uppercase tracking-wider text-gray-500">What went wrong?</h4>
<button onClick={() => setShowOptions(false)} className="text-gray-400 hover:text-gray-600">✕</button>
</div>
<div className="flex flex-col gap-1.5">
{medicalOptions.map((option) => (
<button
key={option}
onClick={() => handleOptionClick(option)}
className="text-left text-xs p-2.5 hover:bg-red-50 dark:hover:bg-red-900/30 rounded-lg border border-transparent hover:border-red-200 transition-all text-gray-700 dark:text-gray-300"
>
{option}
</button>
))}
</div>
</div>
)}
{feedback === 'up' && <span className="text-[10px] text-green-600 font-medium">Feedback sent!</span>}
</div>
)}
</div>
</>
) : (
<>
<UserIcon className="h-6 w-6 min-h-6 min-w-6 my-3 text-gray-500 dark:text-gray-300" />
<div className="bg-blue-500 text-white rounded-lg p-4">
<p className="min-h-6 overflow-wrap-anywhere">{msg.content}</p>
</div>
</>
)}
</div>
);
}
export default function Chat({ messages }) {
const empty = messages.length === 0;
useEffect(() => {
if (window.MathJax) {
window.MathJax.typeset();
}
}, [messages]);
return (
<div
className={`flex-1 p-6 max-w-[960px] w-full ${empty ? "flex flex-col items-center justify-end" : "space-y-4"}`}
>
{empty ? (
<div className="text-xl font-medium text-gray-400">ER Assistant is Ready...</div>
) : (
messages.map((msg, i) => (
<MessageItem key={`message-${i}`} msg={msg} />
))
)}
</div>
);
} |