/
TimurIsm
/
OpenEyes
Обзор
Документация
Войти
/
TimurIsm
/
OpenEyes
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
front/src/components/TestForm.jsx
206 строк
8 KB
TimurIsm
first_commit
16 май 2026, 15:34
16 май 2026, 15:34
4d3f198
Код
Авторство
О чём код?
import React, { useCallback } from 'react'; import { observer } from 'mobx-react-lite'; import AlertMessage from '../components/AlertMessage'; import AddTestInput from '../components/AddTestInput'; import AddTestQuestion from '../components/AddTestQuestion'; import Button from '../buttons/Button'; import {MAX_TYPE_LENGTH, MAX_SHORT_DESC_LENGTH, MAX_FOLDER_LENGTH, MAX_MAIN_IMAGE_LENGTH, MAX_RESULT_LENGTH,} from '../hooks/useTestForm'; const TestForm = observer(({ formData, alertMessage, mode = 'create', onSubmit, onCancel, onInputChange, onResultChange, getResultText, onMainImageFileChange, onMainImageManualChange, onQuestionChange, onQuestionFileChange, onAddQuestion, onRemoveQuestion, validateImagePath, submitButtonText = 'Создать тест', cancelButtonText = 'Отмена', showCancelButton = true }) => { const handleSubmit = useCallback((e) => { e.preventDefault(); onSubmit(); }, [onSubmit]); const sortedQuestions = [...formData.questions].sort((a, b) => a.id - b.id); return ( <form onSubmit={handleSubmit} className="space-y-4"> {mode === 'create' && ( <h1 className="text-2xl font-bold text-center mb-6">Добавление нового теста</h1> )} <AddTestInput label="Название теста" id="type" value={formData.type} onChange={(e) => onInputChange('type', e.target.value, MAX_TYPE_LENGTH)} maxLength={MAX_TYPE_LENGTH} currentLength={formData.type.length} required /> <AddTestInput label="Краткое описание" id="short_description" value={formData.short_description} onChange={(e) => onInputChange('short_description', e.target.value, MAX_SHORT_DESC_LENGTH)} maxLength={MAX_SHORT_DESC_LENGTH} currentLength={formData.short_description.length} textarea required /> <AddTestInput label="Полное описание" id="full_description" value={formData.full_description} onChange={(e) => onInputChange('full_description', e.target.value)} textarea required currentLength={formData.full_description.length} showCounter={true} /> <AddTestInput label="Папка с изображениями" id="folder" value={formData.folder} onChange={(e) => onInputChange('folder', e.target.value, MAX_FOLDER_LENGTH)} maxLength={MAX_FOLDER_LENGTH} currentLength={formData.folder.length} placeholder="Папка с изображениями должна находится в папке public проекта" required /> <div className="mb-6"> <label className="block mb-2 text-lg font-medium text-gray-900"> Главное изображение <span className="text-sm text-gray-500 ml-2"> {formData.main_image.length}/{MAX_MAIN_IMAGE_LENGTH} </span> </label> <input type="text" className={`w-full p-2 border border-gray-300 rounded-md mb-2 ${!formData.folder ? 'bg-gray-100 cursor-not-allowed' : ''}`} value={formData.main_image} onChange={(e) => onMainImageManualChange(e.target.value)} disabled={!formData.folder} placeholder={formData.folder ? "Введите путь к изображению" : "Сначала укажите папку для изображений"} required /> <div className="flex items-center"> <input type="file" className={`block text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold ${!formData.folder ? 'file:bg-gray-200 file:text-gray-500 cursor-not-allowed' : 'file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100'}`} onChange={onMainImageFileChange} accept="image/*" disabled={!formData.folder} /> </div> {formData.main_image && !validateImagePath(formData.main_image) && ( <p className="text-red-500 text-sm mt-1">Папка в пути изображения не совпадает с указанной папкой!</p> )} </div> <div className="space-y-4 mb-6"> <AddTestInput label="Результат при успешном прохождении" id="success_result" value={getResultText('SUCCESS')} onChange={(e) => onResultChange('SUCCESS', e.target.value)} maxLength={MAX_RESULT_LENGTH} currentLength={getResultText('SUCCESS').length} textarea required placeholder="Введите текст, который увидит пользователь при успешном прохождении теста" /> <AddTestInput label="Результат при неудачном прохождении" id="failure_result" value={getResultText('FAILURE')} onChange={(e) => onResultChange('FAILURE', e.target.value)} maxLength={MAX_RESULT_LENGTH} currentLength={getResultText('FAILURE').length} textarea required placeholder="Введите текст, который увидит пользователь при неудачном прохождении теста" /> </div> <div className="space-y-4 mb-4"> <h3 className="text-lg font-semibold"> Вопросы теста <span className="text-sm text-gray-500 ml-2"> {sortedQuestions.length} {sortedQuestions.length === 1 ? 'вопрос' : sortedQuestions.length >= 2 && sortedQuestions.length <= 4 ? 'вопроса' : 'вопросов'} </span> </h3> {sortedQuestions.map((question, index) => { const hasPathError = question.content?.startsWith('/') && !validateImagePath(question.content); return ( <AddTestQuestion key={index} index={index} question={question} folder={formData.folder} handleQuestionChange={onQuestionChange} handleQuestionFileChange={onQuestionFileChange} handleRemoveQuestion={onRemoveQuestion} hasPathError={hasPathError} /> ); })} </div> {alertMessage && ( <div className="mb-4"> <AlertMessage type={alertMessage.type} message={alertMessage.message} /> </div> )} <div className="flex flex-col sm:flex-row justify-between items-stretch sm:items-center gap-6 mt-8"> <div className="w-full sm:w-auto order-1 sm:order-1"> <Button type="button" onClick={onAddQuestion} buttonText="Добавить вопрос" className="w-full sm:w-auto px-6 py-3 bg-blue-500 hover:bg-blue-600 text-white font-medium rounded-lg transition-colors duration-200 shadow-sm hover:shadow" /> </div> <div className="flex flex-col sm:flex-row gap-4 w-full sm:w-auto order-2 sm:order-2"> {showCancelButton && ( <div className="w-full sm:w-auto"> <Button type="button" onClick={onCancel} buttonText={cancelButtonText} className="w-full sm:w-auto px-6 py-3 bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium rounded-lg transition-colors duration-200 shadow-sm hover:shadow" /> </div> )} <div className="w-full sm:w-auto"> <Button type="submit" buttonText={submitButtonText} className="w-full sm:w-auto px-6 py-3 bg-green-500 hover:bg-green-600 text-white font-medium rounded-lg transition-colors duration-200 shadow-sm hover:shadow" /> </div> </div> </div> </form> ); }); export default TestForm;