All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 52s
212 lines
6.4 KiB
JavaScript
212 lines
6.4 KiB
JavaScript
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
|
||
|