Files
play-life/play-life-web/src/components/WordList.jsx

247 lines
7.7 KiB
React
Raw Normal View History

2025-12-29 20:01:55 +03:00
import React, { useState, useEffect } from 'react'
import './WordList.css'
const API_URL = '/api'
function WordList({ onNavigate, dictionaryId, isNewDictionary, refreshTrigger = 0 }) {
const [words, setWords] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState('')
const [dictionary, setDictionary] = useState(null)
const [dictionaryName, setDictionaryName] = useState('')
const [originalDictionaryName, setOriginalDictionaryName] = useState('')
const [isSavingName, setIsSavingName] = useState(false)
const [currentDictionaryId, setCurrentDictionaryId] = useState(dictionaryId)
const [isNewDict, setIsNewDict] = useState(isNewDictionary)
useEffect(() => {
setCurrentDictionaryId(dictionaryId)
setIsNewDict(isNewDictionary)
if (isNewDictionary) {
setLoading(false)
setDictionary(null)
setDictionaryName('')
setOriginalDictionaryName('')
setWords([])
} else if (dictionaryId !== undefined && dictionaryId !== null) {
fetchDictionary()
fetchWords()
} else {
setLoading(false)
setWords([])
}
}, [dictionaryId, isNewDictionary, refreshTrigger])
const fetchDictionary = async () => {
try {
const response = await fetch(`${API_URL}/dictionaries`)
if (!response.ok) {
throw new Error('Ошибка при загрузке словарей')
}
const dictionaries = await response.json()
const dict = dictionaries.find(d => d.id === dictionaryId)
if (dict) {
setDictionary(dict)
setDictionaryName(dict.name)
setOriginalDictionaryName(dict.name)
}
} catch (err) {
console.error('Error fetching dictionary:', err)
}
}
const fetchWords = async () => {
if (isNewDictionary || dictionaryId === undefined || dictionaryId === null) {
setWords([])
setLoading(false)
return
}
await fetchWordsForDictionary(dictionaryId)
}
const fetchWordsForDictionary = async (dictId) => {
try {
setLoading(true)
const url = `${API_URL}/words?dictionary_id=${dictId}`
const response = await fetch(url)
if (!response.ok) {
throw new Error('Ошибка при загрузке слов')
}
const data = await response.json()
setWords(Array.isArray(data) ? data : [])
setError('')
} catch (err) {
setError(err.message)
setWords([])
} finally {
setLoading(false)
}
}
const handleNameChange = (e) => {
setDictionaryName(e.target.value)
}
const handleNameSave = async () => {
if (!dictionaryName.trim()) {
return
}
setIsSavingName(true)
try {
if (isNewDictionary) {
// Create new dictionary
const response = await fetch(`${API_URL}/dictionaries`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: dictionaryName.trim() }),
})
if (!response.ok) {
throw new Error('Ошибка при создании словаря')
}
const newDict = await response.json()
const newDictionaryId = newDict.id
// Update local state immediately
setOriginalDictionaryName(newDict.name)
setDictionaryName(newDict.name)
setDictionary(newDict)
setCurrentDictionaryId(newDictionaryId)
setIsNewDict(false)
// Fetch words for the new dictionary
await fetchWordsForDictionary(newDictionaryId)
// Update navigation to use the new dictionary ID and remove isNewDictionary flag
onNavigate?.('words', { dictionaryId: newDictionaryId, isNewDictionary: false })
} else if (dictionaryId !== undefined && dictionaryId !== null) {
// Update existing dictionary
const response = await fetch(`${API_URL}/dictionaries/${dictionaryId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: dictionaryName.trim() }),
})
if (!response.ok) {
throw new Error('Ошибка при обновлении словаря')
}
setOriginalDictionaryName(dictionaryName.trim())
if (dictionary) {
setDictionary({ ...dictionary, name: dictionaryName.trim() })
}
}
} catch (err) {
setError(err.message)
} finally {
setIsSavingName(false)
}
}
const showSaveButton = dictionaryName.trim() !== '' && dictionaryName.trim() !== originalDictionaryName
if (loading) {
return (
<div className="word-list">
<div className="loading">Загрузка...</div>
</div>
)
}
if (error) {
return (
<div className="word-list">
<div className="error-message">{error}</div>
</div>
)
}
return (
<div className="word-list">
<button
onClick={() => onNavigate?.('test-config')}
className="close-x-button"
title="Закрыть"
>
</button>
{/* Dictionary name input */}
<div className="dictionary-name-input-container">
<input
type="text"
className="dictionary-name-input"
value={dictionaryName}
onChange={handleNameChange}
placeholder="Введите название словаря"
/>
{showSaveButton && (
<button
className="dictionary-name-save-button"
onClick={handleNameSave}
disabled={isSavingName}
title="Сохранить название"
>
</button>
)}
</div>
{/* Show add button and words list:
- If dictionary exists (has dictionaryId), show regardless of name
- If new dictionary (no dictionaryId), show only if name is set */}
{((currentDictionaryId !== undefined && currentDictionaryId !== null && !isNewDict) || (isNewDict && dictionaryName.trim())) && (
<>
{(!words || words.length === 0) ? (
<>
<button onClick={() => onNavigate?.('add-words', { dictionaryId: currentDictionaryId, dictionaryName })} className="add-button">
Добавить
</button>
<div className="empty-state">
<p>Слов пока нет. Добавьте слова через экран "Добавить слова".</p>
</div>
</>
) : (
<>
<button onClick={() => onNavigate?.('add-words', { dictionaryId: currentDictionaryId, dictionaryName })} className="add-button">
Добавить
</button>
<div className="words-grid">
{words.map((word) => (
<div key={word.id} className="word-card">
<div className="word-content">
<div className="word-header">
<h3 className="word-name">{word.name}</h3>
</div>
<div className="word-translation">{word.translation}</div>
{word.description && (
<div className="word-description">{word.description}</div>
)}
</div>
<div className="word-stats">
<span className="stat-success">{word.success || 0}</span>
<span className="stat-separator"> | </span>
<span className="stat-failure">{word.failure || 0}</span>
</div>
</div>
))}
</div>
</>
)}
</>
)}
</div>
)
}
export default WordList