137 lines
4.9 KiB
TypeScript
137 lines
4.9 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { FaSort, FaSortUp, FaSortDown } from "react-icons/fa";
|
|
import { motion, AnimatePresence } from "framer-motion";
|
|
import DateienItem from './DateienItem';
|
|
import { UserFile } from '../../hooks/useFiles';
|
|
import styles from './DateienLists.module.css';
|
|
|
|
// Sort types
|
|
type SortField = 'file_name' | 'action' | 'size' | 'created_at';
|
|
type SortDirection = 'asc' | 'desc';
|
|
|
|
interface DateienUploadsProps {
|
|
files: UserFile[];
|
|
onFileDeleted: () => void;
|
|
onOptimisticDelete?: (fileId: number) => void;
|
|
}
|
|
|
|
const DateienUploads: React.FC<DateienUploadsProps> = ({ files, onFileDeleted, onOptimisticDelete }) => {
|
|
const [sortField, setSortField] = useState<SortField>('created_at');
|
|
const [sortDirection, setSortDirection] = useState<SortDirection>('desc');
|
|
|
|
// Filter files for uploads (user_uploaded)
|
|
const uploadedFiles = files.filter(file => file.source === 'user_uploaded');
|
|
|
|
// Handle sorting
|
|
const handleSort = (field: SortField) => {
|
|
if (field === sortField) {
|
|
setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc');
|
|
} else {
|
|
setSortField(field);
|
|
setSortDirection(field === 'created_at' ? 'desc' : 'asc');
|
|
}
|
|
};
|
|
|
|
// Sort files
|
|
const sortedFiles = [...uploadedFiles].sort((a, b) => {
|
|
let result = 0;
|
|
|
|
switch (sortField) {
|
|
case 'file_name':
|
|
result = a.file_name.localeCompare(b.file_name);
|
|
break;
|
|
case 'action':
|
|
result = a.action.localeCompare(b.action);
|
|
break;
|
|
case 'size':
|
|
const sizeA = a.size ?? 0;
|
|
const sizeB = b.size ?? 0;
|
|
result = sizeA - sizeB;
|
|
break;
|
|
case 'created_at':
|
|
result = new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
|
|
break;
|
|
}
|
|
|
|
return sortDirection === 'asc' ? result : -result;
|
|
});
|
|
|
|
// Helper to render sort icon
|
|
const renderSortIcon = (field: SortField) => {
|
|
if (sortField !== field) return <FaSort className={styles.sortIcon} />;
|
|
return sortDirection === 'asc' ?
|
|
<FaSortUp className={styles.sortIcon} /> :
|
|
<FaSortDown className={styles.sortIcon} />;
|
|
};
|
|
|
|
if (sortedFiles.length === 0) {
|
|
return (
|
|
<motion.div
|
|
className={styles.noFilesMessage}
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
transition={{ duration: 0.3 }}
|
|
>
|
|
<p>Keine hochgeladenen Dateien gefunden.</p>
|
|
</motion.div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<motion.div
|
|
className={styles.filesTable}
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
transition={{ duration: 0.3 }}
|
|
>
|
|
{/* Table Headers */}
|
|
<div className={styles.tableHeader}>
|
|
<div className={styles.headerCell} onClick={() => handleSort('file_name')}>
|
|
<span>Name</span>
|
|
{renderSortIcon('file_name')}
|
|
</div>
|
|
<div className={styles.headerCell} onClick={() => handleSort('action')}>
|
|
<span>Typ</span>
|
|
{renderSortIcon('action')}
|
|
</div>
|
|
<div className={styles.headerCell} onClick={() => handleSort('size')}>
|
|
<span>Größe</span>
|
|
{renderSortIcon('size')}
|
|
</div>
|
|
<div className={styles.headerCell} onClick={() => handleSort('created_at')}>
|
|
<span>Datum</span>
|
|
{renderSortIcon('created_at')}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Files List */}
|
|
<motion.ul
|
|
className={styles.filesList}
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
transition={{ duration: 0.4, delay: 0.1 }}
|
|
>
|
|
<AnimatePresence mode="popLayout">
|
|
{sortedFiles.map((file: UserFile) => (
|
|
<motion.div
|
|
key={file.id}
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
transition={{ duration: 0.2 }}
|
|
layout
|
|
>
|
|
<DateienItem
|
|
file={file}
|
|
onDelete={onFileDeleted}
|
|
onOptimisticDelete={onOptimisticDelete ? () => onOptimisticDelete(file.id) : undefined}
|
|
/>
|
|
</motion.div>
|
|
))}
|
|
</AnimatePresence>
|
|
</motion.ul>
|
|
</motion.div>
|
|
);
|
|
};
|
|
|
|
export default DateienUploads;
|