Добавлен индикатор загрузки на главной странице и обновлен компонент ProtectedRoute для проверки аутентификации. Изменены стили загрузочного оверлея и добавлен анимированный спиннер для улучшения пользовательского опыта.

This commit is contained in:
Fovway 2025-11-02 23:07:56 +07:00
parent f59cd87ede
commit 10f7fe3556
4 changed files with 94 additions and 14 deletions

View File

@ -160,7 +160,75 @@
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="/manifest.json" />
</head> </head>
<body> <body>
<div id="root"></div> <div id="root">
<!-- Индикатор загрузки до монтирования React -->
<div id="initial-loading" style="
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: var(--bg-primary);
color: var(--text-primary);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
">
<style>
#initial-loading-spinner {
width: 50px;
height: 50px;
border: 4px solid transparent;
border-top: 4px solid var(--accent-color, #007bff);
border-right: 4px solid var(--accent-color, #007bff);
border-bottom: 4px solid transparent;
border-left: 4px solid transparent;
border-radius: 50%;
animation: initial-loading-spin 0.8s linear infinite;
opacity: 0.8;
}
@keyframes initial-loading-spin {
to { transform: rotate(360deg); }
}
</style>
<div id="initial-loading-spinner"></div>
</div>
</div>
<script type="module" src="/src/main.tsx"></script> <script type="module" src="/src/main.tsx"></script>
<script>
// Скрываем индикатор загрузки сразу после загрузки DOM
// React удалит этот элемент при первом рендере через createRoot
(function() {
// Используем MutationObserver для отслеживания изменений в #root
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
// Если React начал добавлять элементы в #root, удаляем индикатор
if (mutation.addedNodes.length > 0) {
const loadingEl = document.getElementById('initial-loading');
if (loadingEl && loadingEl.parentNode) {
loadingEl.parentNode.removeChild(loadingEl);
}
observer.disconnect();
}
});
});
// Начинаем наблюдение за изменениями в #root
const root = document.getElementById('root');
if (root) {
observer.observe(root, { childList: true, subtree: true });
// Фолбэк: если через 2 секунды элемент все еще есть, удаляем вручную
setTimeout(function() {
const loadingEl = document.getElementById('initial-loading');
if (loadingEl && loadingEl.parentNode) {
loadingEl.parentNode.removeChild(loadingEl);
}
observer.disconnect();
}, 2000);
}
})();
</script>
</body> </body>
</html> </html>

View File

@ -33,12 +33,10 @@ export const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({
} }
}; };
if (isAuthenticated) { // Всегда проверяем статус аутентификации при монтировании,
checkAuth(); // независимо от начального состояния Redux (localStorage может быть устаревшим)
} else { checkAuth();
setIsChecking(false); }, [dispatch]);
}
}, [dispatch, isAuthenticated]);
if (isChecking) { if (isChecking) {
return <LoadingOverlay />; return <LoadingOverlay />;

View File

@ -4,7 +4,7 @@ export const LoadingOverlay: React.FC = () => {
return ( return (
<div className="loading-overlay"> <div className="loading-overlay">
<div className="loading-content"> <div className="loading-content">
<div className="loading-text">Загрузка...</div> <div className="loading-spinner"></div>
</div> </div>
</div> </div>
); );

View File

@ -4866,14 +4866,28 @@ textarea:focus {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
min-width: 150px; min-width: 80px;
min-height: 80px;
} }
.loading-text { /* Анимированный спиннер */
color: var(--text-color); .loading-spinner {
font-size: 16px; width: 50px;
font-weight: 500; height: 50px;
text-align: center; border: 4px solid transparent;
border-top: 4px solid var(--accent-color, #007bff);
border-right: 4px solid var(--accent-color, #007bff);
border-bottom: 4px solid transparent;
border-left: 4px solid transparent;
border-radius: 50%;
animation: loading-spin 0.8s linear infinite;
opacity: 0.8;
}
@keyframes loading-spin {
to {
transform: rotate(360deg);
}
} }
/* Темная тема для загрузочного оверлея */ /* Темная тема для загрузочного оверлея */