| import React from 'react'; |
| import type { AnalysisResult } from '../types'; |
| import { RiskLevel } from '../types'; |
| import { Spinner } from './Spinner'; |
|
|
| interface ReportCardProps { |
| result: AnalysisResult; |
| } |
|
|
| const RiskBadge: React.FC<{ level: RiskLevel }> = ({ level }) => { |
| const riskStyles = { |
| [RiskLevel.Low]: 'bg-green-500/10 text-green-300 border-green-500/20', |
| [RiskLevel.Medium]: 'bg-yellow-500/10 text-yellow-300 border-yellow-500/20', |
| [RiskLevel.High]: 'bg-red-500/10 text-red-300 border-red-500/20', |
| }; |
| return ( |
| <span className={'px-3 py-1 text-sm font-semibold rounded-full border ${riskStyles[level]} backdrop-blur-sm'}> |
| {level} Risk |
| </span> |
| ); |
| }; |
|
|
| const Section: React.FC<{ title: string; children: React.ReactNode }> = ({ title, children }) => ( |
| <div> |
| <h4 className="font-semibold text-slate-300">{title}</h4> |
| {children} |
| </div> |
| ); |
|
|
| export const ReportCard: React.FC<ReportCardProps> = ({ result }) => { |
| const cardBaseStyle = "border rounded-xl p-4 transition-all duration-500"; |
| |
| const animationStyle = { |
| animation: `fadeIn 0.5s ease-in-out`, |
| animationFillMode: 'forwards', |
| opacity: 0, |
| } as React.CSSProperties; |
|
|
| if (result.status === 'loading') { |
| return ( |
| <div className={`${cardBaseStyle} border-white/10 bg-black/10 animate-pulse flex items-center space-x-4`} style={animationStyle}> |
| <Spinner /> |
| <div> |
| <p className="font-bold text-slate-300">Analyzing Patient {result.patient.patient_id}...</p> |
| <div className="h-4 bg-white/10 rounded w-48 mt-1"></div> |
| </div> |
| </div> |
| ); |
| } |
| |
| if (result.status === 'error') { |
| return ( |
| <div className={`${cardBaseStyle} border-red-500/30 bg-red-900/40`} style={animationStyle}> |
| <div className="flex justify-between items-center"> |
| <h3 className="text-lg font-bold text-red-200">Patient {result.patient.patient_id}</h3> |
| </div> |
| <p className="mt-2 text-red-300"> |
| <strong className="font-semibold">Analysis Failed:</strong> {result.error} |
| </p> |
| </div> |
| ); |
| } |
| |
| const { analysis, patient } = result; |
|
|
| return ( |
| <div className={`${cardBaseStyle} border-white/10 bg-black/10 hover:bg-black/20`} style={animationStyle}> |
| <div className="flex justify-between items-start mb-3"> |
| <h3 className="text-lg font-bold text-white">Patient {patient.patient_id}</h3> |
| <RiskBadge level={analysis.risk_level} /> |
| </div> |
| |
| <div className="space-y-4"> |
| <Section title="Confidence Score"> |
| <div className="w-full bg-white/10 rounded-full h-2.5 my-1"> |
| <div className="bg-cyan-400 h-2.5 rounded-full" style={{ width: `${analysis.confidence_score * 100}%` }}></div> |
| </div> |
| <p className="text-right text-sm text-slate-300 font-medium">{(analysis.confidence_score * 100).toFixed(1)}%</p> |
| </Section> |
| |
| <Section title="Contributing Factors"> |
| <ul className="list-disc list-inside text-slate-300 text-sm space-y-1 mt-1"> |
| {analysis.contributing_factors.map((factor, i) => <li key={i}>{factor}</li>)} |
| </ul> |
| </Section> |
| |
| <Section title="Preventive Recommendations"> |
| <ul className="list-disc list-inside text-slate-300 text-sm space-y-1 mt-1"> |
| {analysis.preventive_recommendations.map((rec, i) => <li key={i}>{rec}</li>)} |
| </ul> |
| </Section> |
| |
| <details className="text-xs text-slate-400"> |
| <summary className="cursor-pointer font-medium hover:text-white">Show Original Data</summary> |
| <div className="mt-2 p-3 bg-black/20 rounded-lg border border-white/10"> |
| <p><strong>Hospital:</strong> {patient.hospital}</p> |
| <p><strong>Age:</strong> {patient.age}</p> |
| <p><strong>Gender:</strong> {patient.gender}</p> |
| <p><strong>Diagnosis:</strong> {patient.primary_diagnosis}</p> |
| <p><strong>Comorbidities:</strong> {patient.comorbidities}</p> |
| <p><strong>Medications:</strong> {patient.medications_at_discharge}</p> |
| <p><strong>Length of Stay:</strong> {patient.length_of_stay_days} days</p> |
| </div> |
| </details> |
| </div> |
| <style>{` |
| @keyframes fadeIn { |
| from { opacity: 0; transform: translateY(10px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| `}</style> |
| </div> |
| ); |
| }; |
|
|