Исправлены отступы в списке слов
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 56s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 56s
This commit is contained in:
@@ -765,7 +765,7 @@ function AppContent() {
|
||||
}, [activeTab])
|
||||
|
||||
// Определяем, нужно ли скрывать нижнюю панель (для fullscreen экранов)
|
||||
const isFullscreenTab = activeTab === 'test' || activeTab === 'add-words' || activeTab === 'task-form' || activeTab === 'wishlist-form' || activeTab === 'wishlist-detail' || activeTab === 'todoist-integration' || activeTab === 'telegram-integration' || activeTab === 'full' || activeTab === 'priorities' || activeTab === 'dictionaries'
|
||||
const isFullscreenTab = activeTab === 'test' || activeTab === 'add-words' || activeTab === 'task-form' || activeTab === 'wishlist-form' || activeTab === 'wishlist-detail' || activeTab === 'todoist-integration' || activeTab === 'telegram-integration' || activeTab === 'full' || activeTab === 'priorities' || activeTab === 'dictionaries' || activeTab === 'words'
|
||||
|
||||
// Определяем отступы для контейнера
|
||||
const getContainerPadding = () => {
|
||||
@@ -788,6 +788,10 @@ function AppContent() {
|
||||
if (activeTab === 'dictionaries') {
|
||||
return 'px-4 md:px-8 py-0'
|
||||
}
|
||||
// Для экрана списка слов используем такие же отступы как для словарей
|
||||
if (activeTab === 'words') {
|
||||
return 'px-4 md:px-8 py-0'
|
||||
}
|
||||
// Для остальных fullscreen экранов без отступов
|
||||
return 'p-0'
|
||||
}
|
||||
|
||||
@@ -89,42 +89,28 @@ function DictionaryList({ onNavigate, refreshTrigger = 0 }) {
|
||||
setSelectedDictionary(null)
|
||||
}
|
||||
|
||||
// Показываем загрузку только при первой инициализации и если нет данных для отображения
|
||||
const shouldShowLoading = loading && !isInitializedRef.current && dictionaries.length === 0
|
||||
|
||||
if (shouldShowLoading) {
|
||||
return (
|
||||
<div className="dictionary-list">
|
||||
<div className="fixed inset-0 bottom-20 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>
|
||||
)
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="dictionary-list">
|
||||
<LoadingError onRetry={fetchDictionaries} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="dictionary-list">
|
||||
{/* Кнопка закрытия */}
|
||||
<button
|
||||
<button
|
||||
className="dictionary-close-button"
|
||||
onClick={() => onNavigate?.('profile')}
|
||||
title="Закрыть"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
|
||||
<div className="dictionaries-grid">
|
||||
{/* Показываем загрузку только при первой инициализации и если нет данных для отображения */}
|
||||
{loading && !isInitializedRef.current && dictionaries.length === 0 ? (
|
||||
<div className="fixed inset-0 bottom-20 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>
|
||||
) : error ? (
|
||||
<LoadingError onRetry={fetchDictionaries} />
|
||||
) : (
|
||||
<div className="dictionaries-grid">
|
||||
{dictionaries.map((dict) => (
|
||||
<div
|
||||
key={dict.id}
|
||||
@@ -151,7 +137,8 @@ function DictionaryList({ onNavigate, refreshTrigger = 0 }) {
|
||||
<div className="add-dictionary-icon">+</div>
|
||||
<div className="add-dictionary-text">Добавить</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedDictionary && (
|
||||
<div className="dictionary-modal-overlay" onClick={closeDictionaryModal}>
|
||||
|
||||
@@ -117,19 +117,7 @@ function FullStatistics({ selectedProject, onClearSelection, data, loading, erro
|
||||
|
||||
const chartData = processData()
|
||||
|
||||
// Показываем loading только если данных нет и идет загрузка
|
||||
if (loading && !chartData) {
|
||||
return (
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
||||
if (error && !chartData) {
|
||||
if (error && !chartData && !loading) {
|
||||
return <LoadingError onRetry={onRetry} />
|
||||
}
|
||||
|
||||
@@ -194,13 +182,6 @@ function FullStatistics({ selectedProject, onClearSelection, data, loading, erro
|
||||
},
|
||||
}
|
||||
|
||||
if (!chartData) {
|
||||
return (
|
||||
<div className="flex justify-center items-center py-16">
|
||||
<div className="text-gray-500 text-lg">Нет данных для отображения</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto">
|
||||
@@ -213,10 +194,25 @@ function FullStatistics({ selectedProject, onClearSelection, data, loading, erro
|
||||
✕
|
||||
</button>
|
||||
)}
|
||||
<div style={{ height: '550px', paddingTop: '100px' }}>
|
||||
<Line data={chartData} options={chartOptions} />
|
||||
</div>
|
||||
<WeekProgressChart data={data} allProjectsSorted={getAllProjectsSorted(data)} currentWeekData={currentWeekData} selectedProject={selectedProject} />
|
||||
{loading && !chartData ? (
|
||||
<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>
|
||||
) : !chartData ? (
|
||||
<div className="flex justify-center items-center py-16">
|
||||
<div className="text-gray-500 text-lg">Нет данных для отображения</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div style={{ height: '550px', paddingTop: '100px' }}>
|
||||
<Line data={chartData} options={chartOptions} />
|
||||
</div>
|
||||
<WeekProgressChart data={data} allProjectsSorted={getAllProjectsSorted(data)} currentWeekData={currentWeekData} selectedProject={selectedProject} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -705,7 +705,6 @@ function TaskDetail({ taskId, onClose, onRefresh, onTaskCompleted, onNavigate })
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{toastMessage && (
|
||||
<Toast
|
||||
@@ -715,7 +714,8 @@ function TaskDetail({ taskId, onClose, onRefresh, onTaskCompleted, onNavigate })
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default TaskDetail
|
||||
|
||||
@@ -765,25 +765,21 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa
|
||||
}
|
||||
}
|
||||
|
||||
if (loadingTask) {
|
||||
return (
|
||||
<div className="task-form">
|
||||
return (
|
||||
<div className="task-form">
|
||||
<button className="close-x-button" onClick={handleCancel}>
|
||||
✕
|
||||
</button>
|
||||
{loadingTask ? (
|
||||
<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="task-form">
|
||||
<button className="close-x-button" onClick={handleCancel}>
|
||||
✕
|
||||
</button>
|
||||
<h2>{taskId ? 'Редактировать задачу' : 'Новая задача'}</h2>
|
||||
) : (
|
||||
<>
|
||||
<h2>{taskId ? 'Редактировать задачу' : 'Новая задача'}</h2>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-group">
|
||||
@@ -1196,14 +1192,17 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
{toastMessage && (
|
||||
<Toast
|
||||
message={toastMessage.text}
|
||||
type={toastMessage.type}
|
||||
onClose={() => setToastMessage(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{toastMessage && (
|
||||
<Toast
|
||||
message={toastMessage.text}
|
||||
type={toastMessage.type}
|
||||
onClose={() => setToastMessage(null)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -595,21 +595,7 @@ function Wishlist({ onNavigate, refreshTrigger = 0, isActive = false, initialBoa
|
||||
)
|
||||
}
|
||||
|
||||
// Показываем loading только если и доски и желания грузятся
|
||||
if (boardsLoading && loading) {
|
||||
return (
|
||||
<div className="wishlist">
|
||||
<div className="fixed inset-0 bottom-20 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>
|
||||
)
|
||||
}
|
||||
|
||||
if (error && items.length === 0) {
|
||||
if (error && items.length === 0 && !boardsLoading && !loading) {
|
||||
return (
|
||||
<div className="wishlist">
|
||||
<BoardSelector
|
||||
@@ -638,7 +624,14 @@ function Wishlist({ onNavigate, refreshTrigger = 0, isActive = false, initialBoa
|
||||
/>
|
||||
|
||||
{/* Основной список */}
|
||||
{loading ? (
|
||||
{boardsLoading && loading ? (
|
||||
<div className="fixed inset-0 bottom-20 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>
|
||||
) : loading ? (
|
||||
<div className="wishlist-loading">
|
||||
<div className="w-8 h-8 border-4 border-indigo-200 border-t-indigo-600 rounded-full animate-spin"></div>
|
||||
</div>
|
||||
|
||||
@@ -298,24 +298,20 @@ function WishlistDetail({ wishlistId, onNavigate, onRefresh, boardId }) {
|
||||
)
|
||||
}
|
||||
|
||||
if (loadingWishlist) {
|
||||
return (
|
||||
<div className="wishlist-detail">
|
||||
<div className="fixed inset-0 bottom-20 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="wishlist-detail">
|
||||
<button className="close-x-button" onClick={() => onNavigate?.('wishlist')}>
|
||||
✕
|
||||
</button>
|
||||
{loadingWishlist && (
|
||||
<div className="fixed inset-0 bottom-20 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>
|
||||
)}
|
||||
<h2>{wishlistItem ? wishlistItem.name : 'Желание'}</h2>
|
||||
|
||||
<div className="wishlist-detail-content">
|
||||
|
||||
@@ -645,25 +645,21 @@ function WishlistForm({ onNavigate, wishlistId, editConditionIndex, newTaskId, b
|
||||
}
|
||||
}
|
||||
|
||||
if (loadingWishlist) {
|
||||
return (
|
||||
<div className="wishlist-form">
|
||||
<div className="fixed inset-0 bottom-20 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="wishlist-form">
|
||||
<button className="close-x-button" onClick={handleCancel}>
|
||||
✕
|
||||
</button>
|
||||
<h2>{wishlistId ? 'Редактировать желание' : 'Новое желание'}</h2>
|
||||
{loadingWishlist ? (
|
||||
<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>
|
||||
) : (
|
||||
<>
|
||||
<h2>{wishlistId ? 'Редактировать желание' : 'Новое желание'}</h2>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-group">
|
||||
@@ -855,14 +851,17 @@ function WishlistForm({ onNavigate, wishlistId, editConditionIndex, newTaskId, b
|
||||
/>
|
||||
)}
|
||||
|
||||
{toastMessage && (
|
||||
<Toast
|
||||
message={toastMessage.text}
|
||||
type={toastMessage.type}
|
||||
onClose={() => setToastMessage(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{toastMessage && (
|
||||
<Toast
|
||||
message={toastMessage.text}
|
||||
type={toastMessage.type}
|
||||
onClose={() => setToastMessage(null)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
.word-list {
|
||||
position: relative;
|
||||
max-width: 42rem; /* max-w-2xl = 672px */
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.close-x-button {
|
||||
|
||||
@@ -161,20 +161,7 @@ function WordList({ onNavigate, dictionaryId, isNewDictionary, refreshTrigger =
|
||||
// Show save button only if name is not empty and has changed
|
||||
const showSaveButton = dictionaryName.trim() !== '' && dictionaryName.trim() !== originalDictionaryName
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="word-list">
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
||||
if (error) {
|
||||
if (error && !loading) {
|
||||
return (
|
||||
<div className="word-list">
|
||||
<LoadingError onRetry={() => {
|
||||
@@ -188,13 +175,21 @@ function WordList({ onNavigate, dictionaryId, isNewDictionary, refreshTrigger =
|
||||
|
||||
return (
|
||||
<div className="word-list">
|
||||
<button
|
||||
onClick={() => onNavigate?.('dictionaries')}
|
||||
<button
|
||||
onClick={() => onNavigate?.('dictionaries')}
|
||||
className="close-x-button"
|
||||
title="Закрыть"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
{loading && (
|
||||
<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>
|
||||
)}
|
||||
|
||||
{/* Dictionary name input */}
|
||||
<div className="dictionary-name-input-container">
|
||||
|
||||
Reference in New Issue
Block a user