create-ex-den-kit
Version:
Create exam projects with Ex-Den-Kit
157 lines (142 loc) • 6.08 kB
JSX
import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import axios from 'axios';
import toast from 'react-hot-toast';
import { useTheme } from '../contexts/ThemeContext';
import { Archive as ArchiveIcon, Calendar, Clock, RotateCcw } from 'lucide-react';
import { format } from 'date-fns';
import { ru } from 'date-fns/locale';
const Archive = () => {
const { config } = useTheme();
const [entries, setEntries] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchArchivedEntries();
}, []);
const fetchArchivedEntries = async () => {
try {
const response = await axios.get('/api/entries/archive');
setEntries(response.data);
} catch (error) {
toast.error('Ошибка при загрузке архива');
} finally {
setLoading(false);
}
};
const handleRestore = async (id) => {
try {
// В реальном приложении здесь был бы API для восстановления
toast.success('Функция восстановления в разработке');
} catch (error) {
toast.error('Ошибка при восстановлении записи');
}
};
if (loading) {
return (
<div className="flex justify-center items-center h-64">
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
className="w-16 h-16 border-4 border-primary border-t-transparent rounded-full"
/>
</div>
);
}
return (
<div>
<div className="flex items-center space-x-3 mb-8">
<ArchiveIcon size={32} className="text-gray-600" />
<h1 className="text-3xl font-bold text-gray-900">Архив записей</h1>
</div>
{entries.length === 0 ? (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="text-center py-12"
>
<div className="text-6xl mb-4 opacity-20">📦</div>
<h3 className="text-xl font-medium text-gray-600 mb-2">
Архив пуст
</h3>
<p className="text-gray-500">
Здесь будут отображаться удаленные и отклоненные записи
</p>
</motion.div>
) : (
<div className="grid gap-4">
<AnimatePresence>
{entries.map((entry, index) => (
<motion.div
key={entry.id}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 20 }}
transition={{ delay: index * 0.05 }}
className="bg-white rounded-lg shadow p-6 opacity-75 hover:opacity-100 transition-opacity"
>
<div className="flex justify-between items-start">
<div className="flex-1">
<div className="flex items-center space-x-3 mb-3">
<span className="px-3 py-1 bg-gray-100 text-gray-600 text-sm rounded-full">
Архив
</span>
{entry.status === 'rejected' && (
<span className="px-3 py-1 bg-red-100 text-red-700 text-sm rounded-full">
Отклонена
</span>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 mb-4">
{Object.entries(entry.data).map(([key, value]) => {
if (key === 'review' || key === 'reviewDate') return null;
const field = config.fields.find(f => f.name === key);
if (!field) return null;
return (
<div key={key}>
<p className="text-sm text-gray-500">{field.label}</p>
<p className="font-medium text-gray-700">{value}</p>
</div>
);
})}
</div>
{entry.rejectionReason && (
<div className="mt-3 p-3 bg-red-50 rounded-lg">
<p className="text-sm text-red-800">
<strong>Причина отклонения:</strong> {entry.rejectionReason}
</p>
</div>
)}
<div className="mt-4 flex items-center justify-between">
<div className="flex items-center space-x-4 text-sm text-gray-500">
<div className="flex items-center space-x-1">
<Calendar size={16} />
<span>
{format(new Date(entry.createdAt), 'dd MMM yyyy', { locale: ru })}
</span>
</div>
<div className="flex items-center space-x-1">
<Clock size={16} />
<span>
{format(new Date(entry.createdAt), 'HH:mm')}
</span>
</div>
</div>
<button
onClick={() => handleRestore(entry.id)}
className="flex items-center space-x-1 text-blue-600 hover:text-blue-700 transition-colors"
>
<RotateCcw size={16} />
<span className="text-sm">Восстановить</span>
</button>
</div>
</div>
</div>
</motion.div>
))}
</AnimatePresence>
</div>
)}
</div>
);
};
export default Archive;