164 lines
4.7 KiB
React
164 lines
4.7 KiB
React
|
|
import React, { useState } from 'react'
|
|||
|
|
import './AddWords.css'
|
|||
|
|
|
|||
|
|
const API_URL = '/api'
|
|||
|
|
|
|||
|
|
function AddWords({ onNavigate, dictionaryId, dictionaryName }) {
|
|||
|
|
const [markdownText, setMarkdownText] = useState('')
|
|||
|
|
const [message, setMessage] = useState('')
|
|||
|
|
const [loading, setLoading] = useState(false)
|
|||
|
|
|
|||
|
|
// Hide add button if dictionary name is not set
|
|||
|
|
const canAddWords = dictionaryName && dictionaryName.trim() !== ''
|
|||
|
|
|
|||
|
|
const parseMarkdownTable = (text) => {
|
|||
|
|
const lines = text.split('\n')
|
|||
|
|
const words = []
|
|||
|
|
let foundTable = false
|
|||
|
|
let headerFound = false
|
|||
|
|
|
|||
|
|
for (let i = 0; i < lines.length; i++) {
|
|||
|
|
const line = lines[i].trim()
|
|||
|
|
|
|||
|
|
// Skip empty lines
|
|||
|
|
if (!line) continue
|
|||
|
|
|
|||
|
|
// Look for table start (markdown table with |)
|
|||
|
|
if (line.includes('|') && line.includes('Слово')) {
|
|||
|
|
foundTable = true
|
|||
|
|
headerFound = true
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Skip separator line (|---|---|)
|
|||
|
|
if (foundTable && line.match(/^\|[\s\-|:]+\|$/)) {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Parse table rows
|
|||
|
|
if (foundTable && headerFound && line.includes('|')) {
|
|||
|
|
const cells = line
|
|||
|
|
.split('|')
|
|||
|
|
.map(cell => (cell || '').trim())
|
|||
|
|
.filter(cell => cell && cell.length > 0)
|
|||
|
|
|
|||
|
|
if (cells.length >= 2) {
|
|||
|
|
// Remove markdown formatting (**bold**, etc.)
|
|||
|
|
const word = cells[0].replace(/\*\*/g, '').trim()
|
|||
|
|
const translation = cells[1].replace(/\*\*/g, '').trim()
|
|||
|
|
|
|||
|
|
if (word && translation) {
|
|||
|
|
words.push({
|
|||
|
|
name: word,
|
|||
|
|
translation: translation,
|
|||
|
|
description: ''
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return words
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleSubmit = async (e) => {
|
|||
|
|
e.preventDefault()
|
|||
|
|
setMessage('')
|
|||
|
|
setLoading(true)
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const words = parseMarkdownTable(markdownText)
|
|||
|
|
|
|||
|
|
if (words.length === 0) {
|
|||
|
|
setMessage('Не удалось найти слова в таблице. Убедитесь, что таблица содержит колонки "Слово" и "Перевод".')
|
|||
|
|
setLoading(false)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Add dictionary_id to each word if dictionaryId is provided
|
|||
|
|
const wordsWithDictionary = words.map(word => ({
|
|||
|
|
...word,
|
|||
|
|
dictionary_id: dictionaryId !== undefined && dictionaryId !== null ? dictionaryId : undefined
|
|||
|
|
}))
|
|||
|
|
|
|||
|
|
const response = await fetch(`${API_URL}/words`, {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify({ words: wordsWithDictionary }),
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error('Ошибка при добавлении слов')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const data = await response.json()
|
|||
|
|
const addedCount = data?.added || 0
|
|||
|
|
setMessage(`Успешно добавлено ${addedCount} слов(а)!`)
|
|||
|
|
setMarkdownText('')
|
|||
|
|
} catch (error) {
|
|||
|
|
setMessage(`Ошибка: ${error.message}`)
|
|||
|
|
} finally {
|
|||
|
|
setLoading(false)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleClose = () => {
|
|||
|
|
onNavigate?.('words', dictionaryId !== undefined && dictionaryId !== null ? { dictionaryId } : {})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="add-words">
|
|||
|
|
<button className="close-x-button" onClick={handleClose}>
|
|||
|
|
✕
|
|||
|
|
</button>
|
|||
|
|
<h2>Добавить слова</h2>
|
|||
|
|
<p className="description">
|
|||
|
|
Вставьте текст в формате Markdown с таблицей, содержащей колонки "Слово" и "Перевод"
|
|||
|
|
</p>
|
|||
|
|
|
|||
|
|
<form onSubmit={handleSubmit}>
|
|||
|
|
<textarea
|
|||
|
|
className="markdown-input"
|
|||
|
|
value={markdownText}
|
|||
|
|
onChange={(e) => setMarkdownText(e.target.value)}
|
|||
|
|
placeholder={`Вот таблица, содержащая только слова и их перевод:
|
|||
|
|
|
|||
|
|
| Слово (Word) | Перевод |
|
|||
|
|
| --- | --- |
|
|||
|
|
| **Adventure** | Приключение |
|
|||
|
|
| **Challenge** | Вызов, сложная задача |
|
|||
|
|
| **Decision** | Решение |`}
|
|||
|
|
rows={15}
|
|||
|
|
/>
|
|||
|
|
|
|||
|
|
{canAddWords && (
|
|||
|
|
<button
|
|||
|
|
type="submit"
|
|||
|
|
className="submit-button"
|
|||
|
|
disabled={loading || !markdownText.trim()}
|
|||
|
|
>
|
|||
|
|
{loading ? 'Добавление...' : 'Добавить слова'}
|
|||
|
|
</button>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
{!canAddWords && (
|
|||
|
|
<div className="message error">
|
|||
|
|
Сначала установите название словаря на экране списка слов
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
</form>
|
|||
|
|
|
|||
|
|
{message && (
|
|||
|
|
<div className={`message ${message.includes('Ошибка') ? 'error' : 'success'}`}>
|
|||
|
|
{message}
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default AddWords
|
|||
|
|
|