sozsoft-platform/ui/src/views/ServiceUnavailable.tsx
2026-08-09 11:52:28 +03:00

135 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useEffect, useState } from 'react'
import classNames from 'classnames'
import { Helmet } from 'react-helmet'
import { Button } from '@/components/ui'
import { APP_NAME } from '@/constants/app.constant'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { pollUntilServerReady } from '@/services/setup.service'
import { useStoreState } from '@/store'
/**
* Uygulama konfigürasyonu alınamadığında gösterilir (tipik olarak veritabanı erişilemez
* durumdayken).
*
* Kullanıcı bilinçli olarak kurulum sihirbazına yönlendirilmez — çalışan bir sistemde
* geçici bir arıza kimseyi kurulum ekranına düşürmemeli. Bunun yerine durum açıkça
* belirtilir, sunucu arka planda izlenir ve toparlandığı anda sayfa kendini yeniler.
*/
const ServiceUnavailable = () => {
const isDark = useStoreState((state) => state.theme.mode) === 'dark'
const [attempt, setAttempt] = useState(0)
// Sunucu toparlandığında kullanıcının sayfayı elle yenilemesi gerekmesin.
useEffect(() => {
const cancel = pollUntilServerReady(
() => window.location.reload(),
(currentAttempt) => setAttempt(currentAttempt),
)
return cancel
}, [])
return (
<div
className={classNames(
'min-h-screen flex flex-col items-center justify-center p-6 transition-colors',
isDark ? 'bg-gray-900 text-gray-100' : 'bg-gray-100 text-gray-900',
)}
>
<Helmet title={`${APP_NAME} — Service unavailable`} />
<div
className={classNames(
'w-full max-w-lg overflow-hidden rounded-xl border shadow-2xl',
isDark ? 'border-gray-700 bg-gray-800' : 'border-gray-200 bg-white',
)}
>
{/* Başlık */}
<div
className={classNames(
'flex items-center gap-3 border-b px-6 py-4',
isDark ? 'border-gray-700 bg-gray-700' : 'border-gray-200 bg-gray-50',
)}
>
<img
src={isDark ? '/img/logo/logo-dark-streamline.png' : '/img/logo/logo-light-streamline.png'}
alt={`${APP_NAME} logo`}
className="h-8 w-8 object-contain"
/>
<span className="text-sm font-semibold tracking-wide">{APP_NAME}</span>
<span
className={classNames(
'ml-auto inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs font-medium uppercase tracking-wider',
isDark
? 'border-amber-400/25 bg-amber-400/10 text-amber-200'
: 'border-amber-500/30 bg-amber-50 text-amber-700',
)}
>
<span
className={classNames(
'h-1.5 w-1.5 animate-pulse rounded-full',
isDark ? 'bg-amber-300' : 'bg-amber-500',
)}
/>
Offline
</span>
</div>
{/* Gövde */}
<div className="px-6 py-7">
<h5 className={isDark ? 'text-gray-100' : 'text-gray-900'}>
Service temporarily unavailable
</h5>
<p className={classNames('mt-3 text-sm leading-relaxed', isDark ? 'text-gray-400' : 'text-gray-600')}>
The application configuration could not be loaded. The server is running, but it cannot
reach its database right now. Your data is not affected the application will come back
automatically once the database is available again.
</p>
<div
className={classNames(
'mt-5 flex items-center gap-2 rounded-lg border px-3 py-2 text-xs',
isDark
? 'border-gray-700 bg-gray-900 text-gray-400'
: 'border-gray-200 bg-gray-50 text-gray-500',
)}
>
<span
className={classNames(
'inline-block h-2 w-2 animate-ping rounded-full',
isDark ? 'bg-sky-400' : 'bg-sky-500',
)}
/>
{attempt > 0
? `Rechecking the connection… (attempt ${attempt})`
: 'Waiting to recheck the connection…'}
</div>
</div>
{/* Aksiyonlar */}
<div
className={classNames(
'flex flex-col-reverse items-stretch gap-3 border-t px-6 py-4 sm:flex-row sm:items-center sm:justify-between',
isDark ? 'border-gray-700 bg-gray-700' : 'border-gray-200 bg-gray-50',
)}
>
<span className={classNames('text-xs', isDark ? 'text-gray-400' : 'text-gray-500')}>
Administrators can run a database migration from the setup page.
</span>
<div className="flex gap-2">
<Button size="sm" onClick={() => window.location.assign(ROUTES_ENUM.setup)}>
Setup page
</Button>
<Button size="sm" variant="solid" onClick={() => window.location.reload()}>
Try again
</Button>
</div>
</div>
</div>
</div>
)
}
export default ServiceUnavailable