310 lines
No EOL
12 KiB
TypeScript
310 lines
No EOL
12 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import { IoClose, IoCheckbox, IoSquareOutline, IoCloudUploadOutline } from 'react-icons/io5';
|
|
import { FaFile } from 'react-icons/fa';
|
|
import { useUserFiles, UserFile, FileInfo } from '../../../hooks/useFiles';
|
|
import DateienUploadTool from './DateienUploadTool';
|
|
import DateienAll from '../DateienAll';
|
|
import DateienUploads from '../DateienUploads';
|
|
import DateienCreated from '../DateienCreated';
|
|
import DateienShared from '../DateienShared';
|
|
import styles from './DateienSelector.module.css';
|
|
|
|
type FileListType = 'all' | 'uploads' | 'created' | 'shared';
|
|
|
|
interface DateienSelectorProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
onFilesSelected: (files: FileInfo[]) => void;
|
|
allowMultiple?: boolean;
|
|
}
|
|
|
|
const DateienSelector: React.FC<DateienSelectorProps> = ({
|
|
isOpen,
|
|
onClose,
|
|
onFilesSelected,
|
|
allowMultiple = true
|
|
}) => {
|
|
const [selectedFiles, setSelectedFiles] = useState<Set<number>>(new Set());
|
|
const [activeTab, setActiveTab] = useState<FileListType>('all');
|
|
const [showUploadTool, setShowUploadTool] = useState(false);
|
|
const { files, loading, error, refetch } = useUserFiles();
|
|
|
|
// Filter files based on source
|
|
const getFilteredFiles = (files: UserFile[], type: FileListType): UserFile[] => {
|
|
switch (type) {
|
|
case 'uploads':
|
|
return files.filter(file => file.source === 'user_uploaded');
|
|
case 'created':
|
|
return files.filter(file => file.source === 'agent_created');
|
|
case 'shared':
|
|
return files.filter(file => file.source === 'shared_with_me');
|
|
case 'all':
|
|
default:
|
|
return files;
|
|
}
|
|
};
|
|
|
|
const filteredFiles = getFilteredFiles(files, activeTab);
|
|
|
|
// Reset selection when tab changes
|
|
useEffect(() => {
|
|
setSelectedFiles(new Set());
|
|
}, [activeTab]);
|
|
|
|
// Reset state when modal closes
|
|
useEffect(() => {
|
|
if (!isOpen) {
|
|
setSelectedFiles(new Set());
|
|
setShowUploadTool(false);
|
|
}
|
|
}, [isOpen]);
|
|
|
|
const handleFileSelect = (fileId: number) => {
|
|
if (allowMultiple) {
|
|
setSelectedFiles(prev => {
|
|
const newSet = new Set(prev);
|
|
if (newSet.has(fileId)) {
|
|
newSet.delete(fileId);
|
|
} else {
|
|
newSet.add(fileId);
|
|
}
|
|
return newSet;
|
|
});
|
|
} else {
|
|
setSelectedFiles(new Set([fileId]));
|
|
}
|
|
};
|
|
|
|
const handleSelectAll = () => {
|
|
if (selectedFiles.size === filteredFiles.length) {
|
|
setSelectedFiles(new Set());
|
|
} else {
|
|
setSelectedFiles(new Set(filteredFiles.map(file => file.id)));
|
|
}
|
|
};
|
|
|
|
const handleConfirmSelection = () => {
|
|
const selectedFileObjects: FileInfo[] = files
|
|
.filter(file => selectedFiles.has(file.id))
|
|
.map(file => ({
|
|
id: file.id,
|
|
name: file.file_name,
|
|
mimeType: deriveMimeTypeFromAction(file.action),
|
|
size: file.size,
|
|
creationDate: file.created_at,
|
|
source: file.source
|
|
}));
|
|
|
|
onFilesSelected(selectedFileObjects);
|
|
onClose();
|
|
};
|
|
|
|
// Helper function to derive MIME type from action (reverse mapping)
|
|
const deriveMimeTypeFromAction = (action: string): string => {
|
|
switch (action.toLowerCase()) {
|
|
case 'bild':
|
|
return 'image/jpeg'; // Default image type
|
|
case 'pdf':
|
|
return 'application/pdf';
|
|
case 'dokument':
|
|
return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
|
|
case 'tabelle':
|
|
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
|
case 'text':
|
|
return 'text/plain';
|
|
case 'video':
|
|
return 'video/mp4'; // Default video type
|
|
case 'audio':
|
|
return 'audio/mpeg'; // Default audio type
|
|
default:
|
|
return 'application/octet-stream'; // Default binary type
|
|
}
|
|
};
|
|
|
|
const handleFileUpload = (file: File) => {
|
|
// Refresh file list after upload
|
|
refetch();
|
|
setShowUploadTool(false);
|
|
};
|
|
|
|
const renderFileListComponent = () => {
|
|
const commonProps = {
|
|
files: filteredFiles,
|
|
onFileDeleted: refetch
|
|
};
|
|
|
|
switch (activeTab) {
|
|
case 'uploads':
|
|
return <DateienUploads {...commonProps} />;
|
|
case 'created':
|
|
return <DateienCreated {...commonProps} />;
|
|
case 'shared':
|
|
return <DateienShared {...commonProps} />;
|
|
case 'all':
|
|
default:
|
|
return <DateienAll {...commonProps} />;
|
|
}
|
|
};
|
|
|
|
const getTabLabel = (type: FileListType) => {
|
|
const counts = {
|
|
all: files.length,
|
|
uploads: files.filter(f => f.source === 'user_uploaded').length,
|
|
created: files.filter(f => f.source === 'agent_created').length,
|
|
shared: files.filter(f => f.source === 'shared_with_me').length
|
|
};
|
|
|
|
const labels = {
|
|
all: 'Alle Dateien',
|
|
uploads: 'Hochgeladen',
|
|
created: 'KI-erstellt',
|
|
shared: 'Geteilt'
|
|
};
|
|
|
|
return `${labels[type]} (${counts[type]})`;
|
|
};
|
|
|
|
if (!isOpen) return null;
|
|
|
|
if (showUploadTool) {
|
|
return (
|
|
<DateienUploadTool
|
|
isOpen={true}
|
|
onClose={() => setShowUploadTool(false)}
|
|
onFileUpload={handleFileUpload}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className={styles.overlay}>
|
|
<motion.div
|
|
className={styles.modal}
|
|
initial={{ opacity: 0, scale: 0.9 }}
|
|
animate={{ opacity: 1, scale: 1 }}
|
|
exit={{ opacity: 0, scale: 0.9 }}
|
|
transition={{ duration: 0.3 }}
|
|
>
|
|
<div className={styles.header}>
|
|
<h2>Dateien auswählen</h2>
|
|
<button className={styles.closeButton} onClick={onClose}>
|
|
<IoClose />
|
|
</button>
|
|
</div>
|
|
|
|
<div className={styles.content}>
|
|
{/* Tab Navigation */}
|
|
<div className={styles.tabNavigation}>
|
|
{(['all', 'uploads', 'created', 'shared'] as FileListType[]).map(tab => (
|
|
<button
|
|
key={tab}
|
|
className={`${styles.tabButton} ${activeTab === tab ? styles.active : ''}`}
|
|
onClick={() => setActiveTab(tab)}
|
|
>
|
|
{getTabLabel(tab)}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* Action Bar */}
|
|
<div className={styles.actionBar}>
|
|
<div className={styles.selectionControls}>
|
|
{allowMultiple && filteredFiles.length > 0 && (
|
|
<button
|
|
className={styles.selectAllButton}
|
|
onClick={handleSelectAll}
|
|
>
|
|
{selectedFiles.size === filteredFiles.length ? (
|
|
<>
|
|
<IoCheckbox /> Alle abwählen
|
|
</>
|
|
) : (
|
|
<>
|
|
<IoSquareOutline /> Alle auswählen
|
|
</>
|
|
)}
|
|
</button>
|
|
)}
|
|
{selectedFiles.size > 0 && (
|
|
<span className={styles.selectionCount}>
|
|
{selectedFiles.size} Datei{selectedFiles.size !== 1 ? 'en' : ''} ausgewählt
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
<button
|
|
className={styles.uploadButton}
|
|
onClick={() => setShowUploadTool(true)}
|
|
>
|
|
<IoCloudUploadOutline />
|
|
Neue Datei hochladen
|
|
</button>
|
|
</div>
|
|
|
|
{/* File List */}
|
|
<div className={styles.fileListContainer}>
|
|
{loading ? (
|
|
<div className={styles.loading}>Dateien werden geladen...</div>
|
|
) : error ? (
|
|
<div className={styles.error}>Fehler beim Laden der Dateien: {error}</div>
|
|
) : filteredFiles.length === 0 ? (
|
|
<div className={styles.noFiles}>Keine Dateien in dieser Kategorie gefunden.</div>
|
|
) : (
|
|
<div className={styles.selectableFileList}>
|
|
{filteredFiles.map(file => (
|
|
<div
|
|
key={file.id}
|
|
className={`${styles.selectableFileItem} ${
|
|
selectedFiles.has(file.id) ? styles.selected : ''
|
|
}`}
|
|
onClick={() => handleFileSelect(file.id)}
|
|
>
|
|
<div className={styles.fileCheckbox}>
|
|
{selectedFiles.has(file.id) ? (
|
|
<IoCheckbox className={styles.checkedIcon} />
|
|
) : (
|
|
<IoSquareOutline className={styles.uncheckedIcon} />
|
|
)}
|
|
</div>
|
|
<div className={styles.fileIcon}>
|
|
<FaFile />
|
|
</div>
|
|
<div className={styles.fileInfo}>
|
|
<div className={styles.fileName}>{file.file_name}</div>
|
|
<div className={styles.fileDetails}>
|
|
{file.action} • {file.size ? `${Math.round(file.size / 1024)} KB` : 'Unbekannte Größe'}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Footer */}
|
|
<div className={styles.footer}>
|
|
<button
|
|
className={styles.cancelButton}
|
|
onClick={onClose}
|
|
>
|
|
Abbrechen
|
|
</button>
|
|
<button
|
|
className={styles.confirmButton}
|
|
onClick={handleConfirmSelection}
|
|
disabled={selectedFiles.size === 0}
|
|
>
|
|
{selectedFiles.size === 0
|
|
? 'Dateien auswählen'
|
|
: `${selectedFiles.size} Datei${selectedFiles.size !== 1 ? 'en' : ''} hinzufügen`
|
|
}
|
|
</button>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default DateienSelector;
|