feat: улучшения системы расписания и UI

- Добавлены таймауты (8 сек) для запросов расписания
- Реализована навигация по неделям с поддержкой параметра wk
- Улучшено кэширование с автоматической очисткой старых записей
- Добавлена валидация параметров в getSchedule
- Улучшен UI загрузки с анимированными сообщениями и предупреждениями
- Оптимизирована обработка ошибок и очистка памяти JSDOM
- Обновлены зависимости проекта
- Добавлена документация для старых файлов
This commit is contained in:
kilyabin
2025-11-30 22:15:07 +04:00
parent d3d33c1e08
commit 3345eb2e3f
10 changed files with 65 additions and 18 deletions

View File

@@ -61,11 +61,15 @@ interface LoadingOverlayProps {
export function LoadingOverlay({ isLoading }: LoadingOverlayProps) {
const [currentMessage, setCurrentMessage] = React.useState<string>('')
const [messageOpacity, setMessageOpacity] = React.useState(0)
const [showError, setShowError] = React.useState(false)
const [errorOpacity, setErrorOpacity] = React.useState(0)
React.useEffect(() => {
if (!isLoading) {
setCurrentMessage('')
setMessageOpacity(0)
setShowError(false)
setErrorOpacity(0)
return
}
@@ -79,6 +83,15 @@ export function LoadingOverlay({ isLoading }: LoadingOverlayProps) {
setCurrentMessage(getRandomMessage())
setMessageOpacity(1)
// Таймер для показа сообщения об ошибке после 5 секунд
const errorTimeout = setTimeout(() => {
setShowError(true)
// Плавное появление с небольшой задержкой для анимации
setTimeout(() => {
setErrorOpacity(1)
}, 50)
}, 5000)
// Меняем сообщение каждые 2 секунды
const interval = setInterval(() => {
// Fade out
@@ -93,6 +106,7 @@ export function LoadingOverlay({ isLoading }: LoadingOverlayProps) {
return () => {
clearInterval(interval)
clearTimeout(errorTimeout)
}
}, [isLoading])
@@ -109,17 +123,32 @@ export function LoadingOverlay({ isLoading }: LoadingOverlayProps) {
aria-hidden={!isLoading}
>
{isLoading && (
<div className="flex flex-col items-center gap-4">
<div className="w-16 h-16">
<Spinner size="large" />
<>
<div className="flex flex-col items-center gap-4">
<div className="w-16 h-16">
<Spinner size="large" />
</div>
<div
className="min-h-[1.5rem] text-center transition-opacity duration-300"
style={{ opacity: messageOpacity }}
>
{currentMessage}
</div>
</div>
<div
className="min-h-[1.5rem] text-center transition-opacity duration-300"
style={{ opacity: messageOpacity }}
>
{currentMessage}
</div>
</div>
{showError && (
<div
className="fixed bottom-8 left-1/2 -translate-x-1/2 bg-background/10 backdrop-blur-sm border border-border/30 rounded-lg p-4 max-w-md mx-4 transition-all duration-500 ease-out"
style={{
opacity: errorOpacity,
transform: `translateX(-50%) translateY(${errorOpacity === 1 ? '0' : '100px'})`
}}
>
<p className="text-sm text-foreground text-center">
Не удается получить актуальное расписание с официального сайта. Возможно, сервер временно недоступен. Будут показаны данные из кэша. Попробуйте обновить страницу позже.
</p>
</div>
)}
</>
)}
</div>
)