192 lines
No EOL
7.9 KiB
TypeScript
192 lines
No EOL
7.9 KiB
TypeScript
import React, { useState } from "react";
|
|
import { FaDownload } from "react-icons/fa";
|
|
import { MdOutlineRemoveRedEye } from "react-icons/md";
|
|
import { Message, Document } from "./dashboardChatAreaTypes";
|
|
import FilePreviewPopup from "./FilePreviewPopup";
|
|
import styles from './DashboardChatArea.module.css';
|
|
|
|
interface MessageItemProps {
|
|
message: Message;
|
|
index: number;
|
|
}
|
|
|
|
// Helper function to format file size
|
|
const formatFileSize = (bytes?: number): string => {
|
|
if (!bytes) return '';
|
|
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
|
|
};
|
|
|
|
// Helper function to get file icon based on type or extension
|
|
const getFileIcon = (type?: string, ext?: string): string => {
|
|
// Use extension first if available, then fall back to MIME type
|
|
const extension = ext?.toLowerCase();
|
|
const mimeType = type?.toLowerCase();
|
|
|
|
// Check extension first
|
|
if (extension) {
|
|
// Images
|
|
if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'].includes(extension)) return '🖼️';
|
|
// Videos
|
|
if (['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', 'mkv'].includes(extension)) return '🎥';
|
|
// Audio
|
|
if (['mp3', 'wav', 'aac', 'flac', 'ogg', 'wma'].includes(extension)) return '🎵';
|
|
// Documents
|
|
if (extension === 'pdf') return '📕';
|
|
if (['doc', 'docx'].includes(extension)) return '📘';
|
|
if (['xls', 'xlsx'].includes(extension)) return '📊';
|
|
if (['ppt', 'pptx'].includes(extension)) return '📋';
|
|
if (['txt', 'md', 'rtf'].includes(extension)) return '📝';
|
|
// Archives
|
|
if (['zip', 'rar', '7z', 'tar', 'gz'].includes(extension)) return '📦';
|
|
// Code files
|
|
if (['js', 'ts', 'jsx', 'tsx', 'html', 'css', 'py', 'java', 'cpp', 'c', 'php'].includes(extension)) return '💻';
|
|
}
|
|
|
|
// Fall back to MIME type if extension didn't match
|
|
if (mimeType) {
|
|
if (mimeType.includes('image')) return '🖼️';
|
|
if (mimeType.includes('video')) return '🎥';
|
|
if (mimeType.includes('audio')) return '🎵';
|
|
if (mimeType.includes('pdf')) return '📕';
|
|
if (mimeType.includes('text')) return '📝';
|
|
if (mimeType.includes('word') || mimeType.includes('document')) return '📘';
|
|
if (mimeType.includes('excel') || mimeType.includes('spreadsheet')) return '📊';
|
|
if (mimeType.includes('powerpoint') || mimeType.includes('presentation')) return '📋';
|
|
if (mimeType.includes('zip') || mimeType.includes('archive')) return '📦';
|
|
}
|
|
|
|
return '📄';
|
|
};
|
|
|
|
const MessageItem: React.FC<MessageItemProps> = ({ message, index }) => {
|
|
const [previewDocument, setPreviewDocument] = useState<Document | null>(null);
|
|
const [isPreviewOpen, setIsPreviewOpen] = useState(false);
|
|
|
|
// Debug logging to see if documents are present
|
|
console.log('MessageItem rendering:', {
|
|
messageId: message.id,
|
|
role: message.role,
|
|
hasDocuments: !!message.documents,
|
|
documentsLength: message.documents?.length || 0,
|
|
documents: message.documents
|
|
});
|
|
|
|
const handleDocumentClick = (document: Document) => {
|
|
// If there's a downloadUrl, use it; otherwise try the url
|
|
const downloadLink = document.downloadUrl || document.url;
|
|
|
|
if (downloadLink) {
|
|
// Open the document in a new tab
|
|
window.open(downloadLink, '_blank');
|
|
}
|
|
};
|
|
|
|
const handlePreview = (document: Document, e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
console.log('handlePreview called with document:', document);
|
|
console.log('document.id:', document.id, 'document.fileId:', document.fileId);
|
|
|
|
// Use fileId if available, otherwise try to use id as fallback
|
|
const fileId = document.fileId || document.id;
|
|
|
|
if (!fileId) {
|
|
console.error('Neither fileId nor id is available on document:', document);
|
|
return;
|
|
}
|
|
|
|
console.log('Using fileId for preview:', fileId, 'type:', typeof fileId);
|
|
|
|
setPreviewDocument(document);
|
|
setIsPreviewOpen(true);
|
|
};
|
|
|
|
const handleClosePreview = () => {
|
|
setIsPreviewOpen(false);
|
|
setPreviewDocument(null);
|
|
};
|
|
|
|
const handleDownload = (document: Document, e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
// TODO: Implement download functionality
|
|
console.log('Download document:', document.name);
|
|
};
|
|
|
|
return (
|
|
<div
|
|
key={message.id || index}
|
|
className={`${styles.message} ${styles[`message_${message.role}`]}`}
|
|
>
|
|
<div className={styles.message_role}>
|
|
{message.role === 'user' ? 'You' :
|
|
message.role === 'assistant' ? 'Assistant' : 'System'}
|
|
</div>
|
|
<div className={styles.message_content}>
|
|
{message.content}
|
|
</div>
|
|
|
|
{message.documents && message.documents.length > 0 && (
|
|
<div className={styles.message_documents}>
|
|
{message.documents.map((document, docIndex) => (
|
|
<div
|
|
key={document.id || docIndex}
|
|
className={styles.document_item}
|
|
onClick={() => handleDocumentClick(document)}
|
|
title={`Click to open ${document.name}`}
|
|
>
|
|
<span className={styles.document_icon}>
|
|
{getFileIcon(document.type, document.ext)}
|
|
</span>
|
|
<div className={styles.document_info}>
|
|
<div className={styles.document_name}>
|
|
{document.ext ? `${document.name}.${document.ext}` : document.name}
|
|
</div>
|
|
<div className={styles.document_meta}>
|
|
{document.size && (
|
|
<span className={styles.document_size}>
|
|
{formatFileSize(document.size)}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className={styles.document_actions}>
|
|
<button
|
|
className={styles.document_action_button}
|
|
onClick={(e) => handlePreview(document, e)}
|
|
title="Preview document"
|
|
>
|
|
<MdOutlineRemoveRedEye />
|
|
</button>
|
|
<button
|
|
className={styles.document_action_button}
|
|
onClick={(e) => handleDownload(document, e)}
|
|
title="Download document"
|
|
>
|
|
<FaDownload />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{message.timestamp && (
|
|
<div className={styles.message_timestamp}>
|
|
{new Date(message.timestamp).toLocaleTimeString()}
|
|
</div>
|
|
)}
|
|
|
|
{/* File Preview Popup */}
|
|
{previewDocument && (
|
|
<FilePreviewPopup
|
|
document={previewDocument}
|
|
isOpen={isPreviewOpen}
|
|
onClose={handleClosePreview}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default MessageItem;
|