/
sweetebin
/
hackaton_26
Обзор
Документация
Войти
/
sweetebin
/
hackaton_26
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
web-form-project/src/components/DataInput.jsx
139 строк
4 KB
AKorostelev
web form project added
28 июл 2026, 10:57
28 июл 2026, 10:57
4339e77
Код
Авторство
О чём код?
import { useRef, useState } from 'react'; import { Alert, Box, Tab, Tabs, TextField, Typography } from '@mui/material'; import UploadFileIcon from '@mui/icons-material/UploadFile'; import DescriptionIcon from '@mui/icons-material/Description'; const ALLOWED_EXT = ['docx', 'md', 'txt', 'csv']; function extOf(name) { return name.split('.').pop().toLowerCase(); } function formatSize(bytes) { if (bytes < 1024) return `${bytes} Б`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} КБ`; return `${(bytes / 1024 / 1024).toFixed(1)} МБ`; } // Ввод данных для анализа: файл (drag-and-drop) или текст. // Сообщает родителю валидный payload { file } | { text } через onChange, иначе null. export default function DataInput({ onChange }) { const [tab, setTab] = useState(0); const [file, setFile] = useState(null); const [text, setText] = useState(''); const [dragOver, setDragOver] = useState(false); const [fileError, setFileError] = useState(null); const inputRef = useRef(null); const emit = (nextTab, nextFile, nextText) => { if (nextTab === 0) onChange(nextFile ? { file: nextFile } : null); else onChange(nextText.trim() ? { text: nextText.trim() } : null); }; const switchTab = (v) => { setTab(v); emit(v, file, text); }; const acceptFile = (f) => { if (!f) return; if (!ALLOWED_EXT.includes(extOf(f.name))) { setFileError( `Формат «.${extOf(f.name)}» не поддерживается. Допустимы: ${ALLOWED_EXT.map((e) => `.${e}`).join(', ')}` ); setFile(null); emit(0, null, text); return; } setFileError(null); setFile(f); emit(0, f, text); }; return ( <Box> <Tabs value={tab} onChange={(_, v) => switchTab(v)} sx={{ mb: 2 }}> <Tab icon={<UploadFileIcon />} iconPosition="start" label="Файл" /> <Tab icon={<DescriptionIcon />} iconPosition="start" label="Текст" /> </Tabs> {tab === 0 && ( <Box> <Box onClick={() => inputRef.current?.click()} onDragOver={(e) => { e.preventDefault(); setDragOver(true); }} onDragLeave={() => setDragOver(false)} onDrop={(e) => { e.preventDefault(); setDragOver(false); acceptFile(e.dataTransfer.files?.[0]); }} sx={{ border: '2px dashed', borderColor: dragOver ? 'primary.main' : 'divider', borderRadius: 2, p: 3, textAlign: 'center', cursor: 'pointer', bgcolor: dragOver ? 'action.hover' : 'transparent', transition: 'background-color .15s, border-color .15s', }} > <UploadFileIcon color="action" sx={{ fontSize: 36, mb: 1 }} /> <Typography> Перетащите файл сюда или <b>выберите на диске</b> </Typography> <Typography variant="body2" color="text.secondary"> Поддерживаются .docx, .md, .txt, .csv </Typography> <input ref={inputRef} type="file" hidden accept={ALLOWED_EXT.map((e) => `.${e}`).join(',')} onChange={(e) => { acceptFile(e.target.files?.[0]); e.target.value = ''; }} /> </Box> {file && ( <Alert severity="info" sx={{ mt: 2 }} onClose={() => { setFile(null); emit(0, null, text); }} > {file.name} · {formatSize(file.size)} </Alert> )} {fileError && ( <Alert severity="error" sx={{ mt: 2 }} onClose={() => setFileError(null)}> {fileError} </Alert> )} </Box> )} {tab === 1 && ( <TextField multiline minRows={6} maxRows={14} fullWidth placeholder="Вставьте текст отзывов или транскрипцию интервью…" value={text} onChange={(e) => { setText(e.target.value); emit(1, file, e.target.value); }} /> )} </Box> ); }