UNPKG

create-ex-den-kit

Version:

Create exam projects with Ex-Den-Kit

157 lines (142 loc) 6.08 kB
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;