Kalıplar
Hatadan kurtarma
Hata türüne göre ne göstereceğiniz, girilen veriyi nasıl koruyacağınız ve kullanıcıyı nasıl yönlendireceğiniz.
Hata kaçınılmazdır; kullanıcıyı çıkmaza sokmak kaçınılabilir. Bu kalıp her hata türü için ne söyleneceğini, neyin korunacağını ve kullanıcının nasıl devam edeceğini belirler.
Ne zaman kullanılır
Bölüm başlığı “Ne zaman kullanılır”- Bir alan, bölüm, işlem, bağlantı veya sayfa hata verdiğinde.
- Bir yükleme başarısız olduğunda ve kullanıcının tekrar denemesi gerektiğinde.
- Sayfa bulunamadığında veya sunucu hata verdiğinde.
Canlı örnek
Bölüm başlığı “Canlı örnek”Kart ilk yüklemede başarısız olur. Tehlike düzeyindeki InlineAlert kartın içinde kalır; sayfanın geri kalanı çalışır. “Tekrar dene” eylemine basınca düğme yüklenir, ardından içerik gelir ve “Yüklendi” toast’ı çıkar.
import { Button, Card, DescriptionList, InlineAlert, useToast } from '@bigbang/ui';import { useEffect, useRef, useState } from 'react';import { type Lang, LangProvider } from '../lang';
const copy = { tr: { title: 'Son raporlar', errorTitle: 'Yüklenemedi', error: 'Raporlar yüklenemedi. Sayfanın geri kalanı çalışmaya devam ediyor.', retry: 'Tekrar dene', retrying: 'Yükleniyor', loaded: 'Yüklendi', items: [ { term: 'Rapor 1', details: '12 kayıt' }, { term: 'Rapor 2', details: '250 kayıt' }, { term: 'Rapor 3', details: '1.000 kayıt' }, ], }, en: { title: 'Recent reports', errorTitle: 'Not loaded', error: 'The reports did not load. The rest of the page keeps working.', retry: 'Try again', retrying: 'Loading', loaded: 'Loaded', items: [ { term: 'Report 1', details: '12 records' }, { term: 'Report 2', details: '250 records' }, { term: 'Report 3', details: '1,000 records' }, ], },};
type State = 'error' | 'loading' | 'ready';
type Props = { lang?: Lang; delay?: number };
function Section({ lang = 'tr', delay = 1200 }: Props) { const t = copy[lang]; const toast = useToast(); const [state, setState] = useState<State>('error'); const bodyRef = useRef<HTMLDivElement>(null); const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined); const hadFocusRef = useRef(false);
useEffect(() => () => clearTimeout(timerRef.current), []);
// The retry button leaves the page on success. If it held focus, hand it to the card body instead of dropping it. useEffect(() => { if (state === 'ready' && hadFocusRef.current) { hadFocusRef.current = false; bodyRef.current?.focus({ preventScroll: true }); } }, [state]);
const retry = () => { if (state === 'loading') return; hadFocusRef.current = true; setState('loading'); timerRef.current = setTimeout(() => { setState('ready'); toast.show({ title: t.loaded, tone: 'success' }); }, delay); };
return ( <Card className="w-full max-w-md"> <Card.Header> <Card.Title>{t.title}</Card.Title> </Card.Header> <Card.Body> <div ref={bodyRef} tabIndex={-1} className="outline-none"> {state === 'ready' ? ( <DescriptionList items={t.items} /> ) : ( <InlineAlert tone="danger" title={t.errorTitle} actions={ <Button variant="secondary" size="sm" loading={state === 'loading'} loadingLabel={t.retrying} onClick={retry}> {t.retry} </Button> } > {t.error} </InlineAlert> )} </div> </Card.Body> </Card> );}
/** * A section whose first load failed (simulated). A danger `InlineAlert` stays inside the card, with a retry action. * The button keeps focus while it retries; on success the card body takes focus once and a success toast says "Loaded". */export function SectionRecovery(props: Props) { return ( <LangProvider lang={props.lang ?? 'tr'}> <Section {...props} /> </LangProvider> );}Tablo kendi hata durumunda (status="error") mesaj ve yeniden deneme düğmesi gösterir.
Raporlar yüklenemedi. Bağlantınızı denetleyip tekrar deneyin. | |
import { createDataTableColumnHelper, DataTable } from '@bigbang/ui';import { useEffect, useMemo, useRef, useState } from 'react';import { type Lang, LangProvider } from '../lang';
const copy = { tr: { caption: 'Rapor listesi', name: 'Ad', count: 'Kayıt', error: 'Raporlar yüklenemedi. Bağlantınızı denetleyip tekrar deneyin.', retry: 'Tekrar dene', item: 'Rapor', }, en: { caption: 'Report list', name: 'Name', count: 'Records', error: 'The reports did not load. Check your connection and try again.', retry: 'Try again', item: 'Report', },};
type Row = { id: string; name: string; count: number };
const helper = createDataTableColumnHelper<Row>();const counts = [12, 250, 1000, 48];
/** A table whose load failed: `status="error"` shows the message and a retry button; retry shows the loading rows, then the data. */export function TableRecovery({ lang = 'tr', delay = 1200 }: { lang?: Lang; delay?: number }) { const t = copy[lang]; const [status, setStatus] = useState<'error' | 'loading' | 'ready'>('error'); const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined); useEffect(() => () => clearTimeout(timerRef.current), []);
const columns = useMemo( () => [ helper.accessor('name', { header: t.name, meta: { rowHeader: true } }), helper.accessor('count', { header: t.count, meta: { numeric: true } }), ], [t], ); const data = useMemo<Row[]>(() => counts.map((count, index) => ({ id: `r${index + 1}`, name: `${t.item} ${index + 1}`, count })), [t]);
const retry = () => { setStatus('loading'); timerRef.current = setTimeout(() => setStatus('ready'), delay); };
return ( <LangProvider lang={lang}> <div className="w-full max-w-xl"> <DataTable columns={columns} data={status === 'ready' ? data : []} getRowId={(row) => row.id} caption={t.caption} status={status} onRetry={retry} messages={{ error: t.error, retry: t.retry }} /> </div> </LangProvider> );}Sayfa düzeyindeki hata ResultState ile (tehlike düzeyi) ne olduğunu, bir hata kodunu, yeniden denemeyi ve ana sayfaya bağlantıyı verir.
Hata: Bir sorun oluştu
Sayfayı şu an açamıyoruz. Biraz sonra yeniden deneyin; sorun sürerse destek ekibine aşağıdaki kodu iletin.
import { Button, Link, ResultState, Text } from '@bigbang/ui';import { useEffect, useRef, useState } from 'react';import { type Lang, LangProvider } from '../lang';
const copy = { tr: { 'not-found': { title: 'Sayfa bulunamadı', text: 'Aradığınız sayfa taşınmış veya silinmiş olabilir. Adresi denetleyin ya da ana sayfaya dönün.' }, server: { title: 'Bir sorun oluştu', text: 'Sayfayı şu an açamıyoruz. Biraz sonra yeniden deneyin; sorun sürerse destek ekibine aşağıdaki kodu iletin.' }, code: 'Hata kodu', retry: 'Tekrar dene', retrying: 'Yükleniyor', home: 'Ana sayfaya dön', }, en: { 'not-found': { title: 'Page not found', text: 'The page may have moved or been removed. Check the address or go back to the home page.' }, server: { title: 'Something went wrong', text: 'We cannot open this page right now. Try again in a moment; if it continues, send the code below to support.' }, code: 'Error code', retry: 'Try again', retrying: 'Loading', home: 'Go to the home page', },};
/** * A page-level error with `ResultState` (tone danger): what happened, what to do, a reference code for support * and a way home. A server error also offers a retry (simulated: it stays loading for `delay` ms, then fails again). * The heading is level 1 because the demo is a whole page. */export function PageError({ lang = 'tr', kind = 'server', code = '5F2A', delay = 1200,}: { lang?: Lang; kind?: 'not-found' | 'server'; code?: string; delay?: number;}) { const t = copy[lang]; const message = t[kind]; const [retrying, setRetrying] = useState(false); const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined); useEffect(() => () => clearTimeout(timerRef.current), []);
const retry = () => { setRetrying(true); timerRef.current = setTimeout(() => setRetrying(false), delay); };
return ( <LangProvider lang={lang}> <div className="w-full max-w-lg"> <ResultState tone="danger" headingLevel={1} title={message.title} description={message.text} actions={ kind === 'server' ? ( <> <Button loading={retrying} loadingLabel={t.retrying} onClick={retry}> {t.retry} </Button> <Link href="/">{t.home}</Link> </> ) : ( <Link href="/">{t.home}</Link> ) } > {kind === 'server' ? ( <Text size="sm" tone="subtle"> {t.code}: {code} </Text> ) : null} </ResultState> </div> </LangProvider> );}Kurallar
Bölüm başlığı “Kurallar”| Hata türü | Ne yapılır |
|---|---|
| Alan hatası | Alanın altında, nasıl düzeltileceğiyle birlikte. Ayrıntı: Formlar ve doğrulama. |
| Bölüm yüklemesi başarısız | Sayfanın geri kalanı çalışır. Bölümde tehlike düzeyinde InlineAlert ve “Tekrar dene” eylemi gösterilir. |
| İşlem başarısız | Kullanıcının girdisi korunur. Ne olduğu, ne yapılacağı yazılır ve yeniden deneme sunulur. |
| Bağlantı koptu veya zaman aşımı | Otomatik yeniden deneme 1 sn, 2 sn, 4 sn arayla; sonra elle “Tekrar dene”. |
| Kısmi hata | Sayfa çalışır ama bir kısmı eksikse uyarı düzeyinde InlineAlert; sayfa geneli etkileniyorsa Banner. |
| Sayfa düzeyi | ResultState (tehlike): bulunamadı veya sunucu hatası; “Hata kodu: 5F2A” gibi başvuru kodu, “Tekrar dene” ve ana sayfa bağlantısı. |
| Odak | Yeniden denerken düğmede kalır; başarıdan sonra sayfa başına atlamaz. |
| Duyuru | Sonradan görünen hata role="alert"; başarılı kurtarma nazik “Yüklendi” toast’ı. |
Davranış
Bölüm başlığı “Davranış”- Bir bölüm başarısız olur. Hata yalnızca o bölümde görünür; sayfanın geri kalanı etkilenmez.
- Kullanıcı “Tekrar dene” düğmesine basar. Düğme yüklenir ve odağı korur.
- Başarıda içerik gelir, “Yüklendi” toast’ı nazik duyurulur. Odak, düğme açıkken oradaysa bölümün gövdesine geçer; sayfa kaymaz.
- Başarısızlıkta hata yerinde kalır ve düğme yeniden kullanılabilir olur.
- Bağlantı koptuğunda otomatik yeniden deneme 1 sn, 2 sn ve 4 sn arayla yapılır; üçüncü başarısızlıktan sonra yalnızca elle denenir.
- Sayfa düzeyinde
ResultStatekullanıcıya sayfanın neden açılamadığını, başvuru kodunu, “Tekrar dene” düğmesini ve ana sayfa bağlantısını verir.
İşlem başarısızsa girilen metin ve seçimler silinmez. Kaydetme akışı için Kaydetme kalıbına bakın.
Erişilebilirlik
Bölüm başlığı “Erişilebilirlik”- Sonradan görünen hata
InlineAlertiçindeannounceilerole="alert"taşır; ekran okuyucu bir kez okur. Sayfa ile birlikte gelen hata duyurulmaz, sayfanın parçasıdır. - “Tekrar dene” düğmesi yüklenirken odakta kalır; Button
loadingdurumunda da odaklanabilir kalır. - Başarıdan sonra “Yüklendi” toast’ı nazik duyurulur.
- Hata simgeyle ve gizli “Hata:” önekiyle verilir; renk tek başına anlam taşımaz.
ResultStatebaşlığı da aynı öneki taşır. - Hata kodu seçilebilir metindir; kullanıcı kopyalayıp destek ekibine iletebilir.
İçerik
Bölüm başlığı “İçerik”- Ne olduğunu ve ne yapılacağını yazın: “Raporlar yüklenemedi. Bağlantınızı denetleyip tekrar deneyin.”
- “Bir hata oluştu” gibi belirsiz ifadelerden ve suçlayıcı dilden kaçının.
- Düğme fiille başlar: “Tekrar dene”.
- Başvuru kodu kısa ve sabit biçimdedir: “Hata kodu: 5F2A”.
Hareket
Bölüm başlığı “Hareket”| Öğe | Hareket | Preset |
|---|---|---|
| Satır içi hata | Kapanırken solma | motion.collapsible |
| Yeniden dene düğmesi | Basınca 0,97 ölçek; yüklenirken Spinner | motion.press |
| İçeriğin gelişi | 110 ms solma | motion.reveal |
Hareketi azaltma tercihinde kaymalar kalkar, yalnızca kısa solma kalır.
Kullanılan bileşenler
Bölüm başlığı “Kullanılan bileşenler”Card, InlineAlert, Banner, ResultState, Toast, DataTable, Button, Link. İlgili kalıplar: Yükleme durumları, Bildirimler, Formlar ve doğrulama.