İçeriğe geç
BigBang

Sık açılanlar

    ↑↓Gezin↵AçEscKapat
    EN

    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.

    • 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.

    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.

    Canlı örnek, StorybookStorybook'ta aç

    Son raporlar

    Hata: Yüklenemedi

    Raporlar yüklenemedi. Sayfanın geri kalanı çalışmaya devam ediyor.
    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.

    Canlı örnek, StorybookStorybook'ta aç
    Rapor listesi
    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.

    Canlı örnek, StorybookStorybook'ta aç

    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.

    Hata kodu: 5F2A
    Ana sayfaya dön
    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>
    );
    }
    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’ı.
    Raporlar yüklenemedi.
    YapınMesaj ne olduğunu söyler ve eylem sunar.
    Bir hata oluştu.
    YapmayınEylemsiz mesaj kullanıcıyı çıkmaza sokar.
    1. 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.
    2. Kullanıcı “Tekrar dene” düğmesine basar. Düğme yüklenir ve odağı korur.
    3. 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.
    4. Başarısızlıkta hata yerinde kalır ve düğme yeniden kullanılabilir olur.
    5. 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.
    6. Sayfa düzeyinde ResultState kullanı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.

    • Sonradan görünen hata InlineAlert içinde announce ile role="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 loading durumunda 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. ResultState başlığı da aynı öneki taşır.
    • Hata kodu seçilebilir metindir; kullanıcı kopyalayıp destek ekibine iletebilir.
    • 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”.
    Öğ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.

    Card, InlineAlert, Banner, ResultState, Toast, DataTable, Button, Link. İlgili kalıplar: Yükleme durumları, Bildirimler, Formlar ve doğrulama.