131 lines
4 KiB
TypeScript
131 lines
4 KiB
TypeScript
import React, { useMemo } from 'react';
|
|
import { MessageDocument, Message } from '../MessagesTypes';
|
|
import { formatFileSize } from '../MessageUtils';
|
|
import { ViewActionButton, DeleteActionButton, RemoveActionButton } from '../../../FormGenerator/ActionButtons';
|
|
import { WorkflowFile } from '../../../../hooks/usePlayground';
|
|
import styles from '../Messages.module.css';
|
|
|
|
export interface DocumentItemProps {
|
|
document: MessageDocument;
|
|
message: Message;
|
|
className?: string;
|
|
onFileDelete?: (file: WorkflowFile) => Promise<void>;
|
|
onFileRemove?: (file: WorkflowFile) => Promise<void>;
|
|
onFileView?: (file: WorkflowFile) => Promise<void>;
|
|
deletingFiles?: Set<string>;
|
|
previewingFiles?: Set<string>;
|
|
removingFiles?: Set<string>;
|
|
workflowId?: string;
|
|
}
|
|
|
|
/**
|
|
* Renders a single document attachment with action buttons
|
|
*/
|
|
export const DocumentItem: React.FC<DocumentItemProps> = ({
|
|
document,
|
|
message: _message,
|
|
className,
|
|
onFileDelete,
|
|
onFileRemove,
|
|
onFileView,
|
|
deletingFiles = new Set(),
|
|
previewingFiles = new Set(),
|
|
removingFiles = new Set(),
|
|
workflowId: _workflowId
|
|
}) => {
|
|
// Convert MessageDocument to WorkflowFile format for compatibility with action buttons
|
|
const workflowFile: WorkflowFile = useMemo(() => ({
|
|
id: document.id,
|
|
fileId: document.fileId,
|
|
fileName: document.fileName,
|
|
fileSize: document.fileSize,
|
|
mimeType: document.mimeType,
|
|
messageId: document.messageId,
|
|
source: 'user_uploaded' // Default to user_uploaded, can be enhanced later
|
|
}), [document]);
|
|
|
|
const isDeleting = deletingFiles.has(document.fileId);
|
|
const isPreviewing = previewingFiles.has(document.fileId);
|
|
const isRemoving = removingFiles.has(document.fileId);
|
|
|
|
// Create hookData object for action buttons
|
|
const hookData = useMemo(() => ({
|
|
handleDelete: async (_fileId: string) => {
|
|
if (onFileDelete) {
|
|
await onFileDelete(workflowFile);
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
removeOptimistically: () => {
|
|
// Handled by parent component
|
|
},
|
|
refetch: async () => {
|
|
// Refetch handled by parent
|
|
},
|
|
deletingItems: deletingFiles,
|
|
previewingFiles: previewingFiles,
|
|
removingItems: removingFiles
|
|
}), [onFileDelete, workflowFile, deletingFiles, previewingFiles, removingFiles]);
|
|
|
|
const handleView = async () => {
|
|
if (onFileView) {
|
|
await onFileView(workflowFile);
|
|
}
|
|
};
|
|
|
|
const handleRemove = async () => {
|
|
if (onFileRemove) {
|
|
await onFileRemove(workflowFile);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className={`${styles.documentItem} ${className || ''}`}>
|
|
<div className={styles.documentIcon}>📎</div>
|
|
<div className={styles.documentInfo}>
|
|
<div className={styles.documentName}>{document.fileName}</div>
|
|
<div className={styles.documentMeta}>
|
|
{formatFileSize(document.fileSize)} • {document.mimeType}
|
|
</div>
|
|
</div>
|
|
{(onFileView || onFileDelete || onFileRemove) && (
|
|
<div style={{ display: 'flex', gap: '4px', flexShrink: 0 }}>
|
|
{onFileView && (
|
|
<ViewActionButton
|
|
row={workflowFile}
|
|
onView={handleView}
|
|
disabled={isDeleting || isRemoving}
|
|
loading={isPreviewing}
|
|
hookData={hookData}
|
|
idField="fileId"
|
|
nameField="fileName"
|
|
typeField="mimeType"
|
|
/>
|
|
)}
|
|
{onFileRemove && (
|
|
<RemoveActionButton
|
|
row={workflowFile}
|
|
onRemove={handleRemove}
|
|
disabled={isDeleting}
|
|
loading={isRemoving}
|
|
hookData={hookData}
|
|
idField="fileId"
|
|
loadingStateName="removingItems"
|
|
/>
|
|
)}
|
|
{onFileDelete && (
|
|
<DeleteActionButton
|
|
row={workflowFile}
|
|
hookData={hookData}
|
|
idField="fileId"
|
|
operationName="handleDelete"
|
|
loadingStateName="deletingItems"
|
|
/>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|