UNPKG

create-ex-den-kit

Version:

Create exam projects with Ex-Den-Kit

204 lines (185 loc) 6.2 kB
import React, { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { motion } from 'framer-motion'; import { useForm } from 'react-hook-form'; import axios from 'axios'; import toast from 'react-hot-toast'; import { useTheme } from '../contexts/ThemeContext'; import { Save, ArrowLeft } from 'lucide-react'; const CreateEntry = () => { const { config } = useTheme(); const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false); const { register, handleSubmit, formState: { errors }, } = useForm(); const onSubmit = async (data) => { setIsLoading(true); try { await axios.post('/api/entries', { type: config.name, data, }); toast.success('Запись успешно создана!'); navigate('/dashboard'); } catch (error) { toast.error('Ошибка при создании записи'); } finally { setIsLoading(false); } }; const renderField = (field) => { switch (field.type) { case 'text': case 'email': case 'tel': case 'date': case 'time': return ( <input type={field.type} {...register(field.name, { required: field.required ? `${field.label} обязательно` : false, })} className="input-field" placeholder={field.placeholder || field.label} /> ); case 'number': return ( <input type="number" {...register(field.name, { required: field.required ? `${field.label} обязательно` : false, min: field.min, max: field.max, })} className="input-field" placeholder={field.placeholder || field.label} min={field.min} max={field.max} /> ); case 'select': return ( <select {...register(field.name, { required: field.required ? `${field.label} обязательно` : false, })} className="input-field" > <option value="">Выберите {field.label.toLowerCase()}</option> {field.options.map((option) => ( <option key={option} value={option}> {option} </option> ))} </select> ); case 'radio': return ( <div className="space-y-3"> {field.options.map((option) => ( <label key={option} className="flex items-center space-x-3 cursor-pointer p-3 rounded-lg hover:bg-gray-50 transition-colors" > <input type="radio" {...register(field.name, { required: field.required ? `${field.label} обязательно` : false, })} value={option} className="w-5 h-5 text-primary focus:ring-primary" /> <span className="text-gray-700">{option}</span> </label> ))} </div> ); case 'textarea': return ( <textarea {...register(field.name, { required: field.required ? `${field.label} обязательно` : false, })} className="input-field resize-none" rows="4" placeholder={field.placeholder || field.label} /> ); default: return null; } }; return ( <div className="max-w-2xl mx-auto"> <div className="flex items-center space-x-4 mb-8"> <button onClick={() => navigate(-1)} className="p-2 hover:bg-gray-100 rounded-lg transition-colors" > <ArrowLeft size={24} /> </button> <h1 className="text-3xl font-bold text-gray-900">Создать запись</h1> </div> <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} className="card" > <form onSubmit={handleSubmit(onSubmit)} className="space-y-6"> {config.fields.map((field, index) => ( <motion.div key={field.name} initial={{ opacity: 0, x: -20 }} animate={{ opacity: 1, x: 0 }} transition={{ delay: index * 0.1 }} > <label className="block text-sm font-medium text-gray-700 mb-2"> {field.label} {field.required && <span className="text-red-500 ml-1">*</span>} </label> {renderField(field)} {errors[field.name] && ( <p className="error-message">{errors[field.name].message}</p> )} </motion.div> ))} <div className="flex space-x-4 pt-6"> <motion.button whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} type="submit" disabled={isLoading} className="flex-1 btn-primary flex items-center justify-center space-x-2" > {isLoading ? ( <motion.div animate={{ rotate: 360 }} transition={{ duration: 1, repeat: Infinity, ease: 'linear' }} className="w-5 h-5 border-2 border-white border-t-transparent rounded-full" /> ) : ( <> <Save size={20} /> <span>Отправить</span> </> )} </motion.button> <button type="button" onClick={() => navigate(-1)} className="flex-1 px-6 py-3 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors" > Отмена </button> </div> </form> </motion.div> </div> ); }; export default CreateEntry;