sozsoft-platform/ui/src/views/ServiceUnavailable.tsx

141 lines
5.2 KiB
TypeScript
Raw Normal View History

import { useEffect, useState } from 'react'
import classNames from 'classnames'
import PageTitle from '@/components/shared/PageTitle'
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.
2026-08-14 11:32:31 +00:00
*
* Metinler bilinçli olarak `translate` ile lokalize EDİLMEZ: bu ekran yalnızca
* konfigürasyon (ve onunla gelen lokalizasyon sözlüğü) yüklenemediğinde render edilir,
* dolayısıyla `translate` anahtarın kendisini döndürürdü. Açılış öncesi tüm ekranlar
* bu nedenle sabit İngilizce tutulur.
*/
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',
)}
>
<PageTitle title="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