Files
play-life/play-life-web/src/components/AddWords.jsx
poignatov 24be9fad27
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 52s
Версия 3.25.0: исправлено добавление слов в словарь
2026-01-20 22:16:08 +03:00

212 lines
6.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect } from 'react'
import { useAuth } from './auth/AuthContext'
import './AddWords.css'
const API_URL = '/api'
function AddWords({ onNavigate, dictionaryId, dictionaryName }) {
const { authFetch } = useAuth()
const [markdownText, setMarkdownText] = useState('')
const [message, setMessage] = useState('')
const [loading, setLoading] = useState(false)
const [currentDictionaryName, setCurrentDictionaryName] = useState(dictionaryName || '')
const [dictionaryLoading, setDictionaryLoading] = useState(false)
// Fetch dictionary name if not provided and dictionaryId exists
useEffect(() => {
if (dictionaryName) {
setCurrentDictionaryName(dictionaryName)
} else if (dictionaryId) {
fetchDictionaryName(dictionaryId)
}
}, [dictionaryId, dictionaryName])
const fetchDictionaryName = async (dictId) => {
if (!dictId) return
setDictionaryLoading(true)
try {
const response = await authFetch(`${API_URL}/dictionaries`)
if (!response.ok) {
throw new Error('Ошибка при загрузке словарей')
}
const dictionaries = await response.json()
const dict = dictionaries.find(d => d.id === dictId)
if (dict) {
setCurrentDictionaryName(dict.name)
}
} catch (err) {
console.error('Error fetching dictionary name:', err)
} finally {
setDictionaryLoading(false)
}
}
// Hide add button if dictionary name is not set
const canAddWords = currentDictionaryName && currentDictionaryName.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 authFetch(`${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 } : {})
}
// Show loading state while fetching dictionary name
if (dictionaryLoading) {
return (
<div className="add-words">
<div className="fixed inset-0 flex justify-center items-center">
<div className="flex flex-col items-center">
<div className="w-12 h-12 border-4 border-indigo-200 border-t-indigo-600 rounded-full animate-spin mb-4"></div>
<div className="text-gray-600 font-medium">Загрузка...</div>
</div>
</div>
</div>
)
}
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