ui-nyla/src/components/Dateien/DateienAll.tsx

142 lines
5.2 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' | 'source';
type SortDirection = 'asc' | 'desc';
interface DateienAllProps {
files: UserFile[];
onFileDeleted: () => void;
onOptimisticDelete?: (fileId: number) => void;
}
const DateienAll: React.FC<DateienAllProps> = ({ files, onFileDeleted, onOptimisticDelete }) => {
const [sortField, setSortField] = useState<SortField>('created_at');
const [sortDirection, setSortDirection] = useState<SortDirection>('desc');
// Handle sorting
const handleSort = (field: SortField) => {
if (field === sortField) {
setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc');
} else {
setSortField(field);
setSortDirection(field === 'created_at' ? 'desc' : 'asc');
}
};
// Sort all files
const sortedFiles = [...files].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;
case 'source':
result = a.source?.localeCompare(b.source ?? '') ?? 0;
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 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, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{
opacity: 0,
scale: 0.95,
x: -50,
transition: { duration: 0.2, ease: "easeIn" }
}}
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 DateienAll;