create-ex-den-kit
Version:
Create exam projects with Ex-Den-Kit
204 lines (185 loc) • 6.2 kB
JSX
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;