Şablonlar
Gösterge paneli
Tarih aralığı, dört gösterge kartı, eğilim grafiği ve son kayıtlar tablosu.
Gösterge paneli bloğu AppShell şablonu içinde bir özet sayfası kurar. Tarih aralığı değişince rakamlar yer değiştirir; grafik ve tablo yeniden giriş hareketi yapmaz.
Önizleme
Bölüm başlığı “Önizleme”Tarih aralığını değiştirin. Sayfa başlığındaki marka ve mod seçimi önizlemeyi de değiştirir. İkinci önizleme ilk yükleme durumudur.
pnpm add @bigbang/ui| Veri | Tür | Not |
|---|---|---|
| Göstergeler | { active, open, done, cycle, delta } |
Uygulama Intl ile biçimler; StatCard sayı biçimlemez. |
| Grafik serileri | ChartSeries[] |
En çok 8 seri; renkler chart.categorical-* token’larından. |
| Son kayıtlar | Row[] |
DataTable status ile yükleniyor, boş ve hata durumlarını gösterir. |
loading |
boolean |
Bütün bölgeleri ilk yükleme durumunda gösterir. |
import { Button, Chart, createDataTableColumnHelper, DataTable, Grid, Icon, SegmentedControl, StatCard, StatusBadge } from '@bigbang/ui';import type { ChartSeries, StatusKey } from '@bigbang/ui';import { useMemo, useState } from 'react';import type { Lang } from '../../patterns/lang';import { AppShellTemplate } from '../../templates/app-shell/AppShellTemplate';
const copy = { tr: { title: 'Genel bakış', description: 'Ekibinizin projeleri ve görevleri.', range: 'Tarih aralığı', ranges: { '7': '7 gün', '30': '30 gün', '90': '90 gün' }, report: 'Rapor oluştur', active: 'Aktif proje', open: 'Açık görev', done: 'Tamamlanan görev', cycle: 'Ortalama süre', days: (value: string) => `${value} gün`, period: 'önceki döneme göre', chart: 'Açılan ve kapanan görevler', chartDescription: 'Haftalık görev sayısı', opened: 'Açılan', closed: 'Kapanan', week: (index: number) => `${index}. hafta`, recent: 'Son kayıtlar', name: 'Kayıt', status: 'Durum', owner: 'Ekip', updated: 'Güncellendi', record: (index: number) => `Kayıt ${index}`, team: (index: number) => `Ekip ${index}`, }, en: { title: 'Overview', description: "Your team's projects and tasks.", range: 'Date range', ranges: { '7': '7 days', '30': '30 days', '90': '90 days' }, report: 'Create report', active: 'Active projects', open: 'Open tasks', done: 'Completed tasks', cycle: 'Average cycle time', days: (value: string) => `${value} days`, period: 'against the previous period', chart: 'Opened and closed tasks', chartDescription: 'Tasks per week', opened: 'Opened', closed: 'Closed', week: (index: number) => `Week ${index}`, recent: 'Recent records', name: 'Record', status: 'Status', owner: 'Team', updated: 'Updated', record: (index: number) => `Record ${index}`, team: (index: number) => `Team ${index}`, },};
type Range = '7' | '30' | '90';
// Round demo figures per range; an app passes its own query results.const figures: Record<Range, { active: number; open: number; done: number; cycle: number; delta: number }> = { '7': { active: 24, open: 120, done: 300, cycle: 3.5, delta: 8 }, '30': { active: 26, open: 140, done: 1200, cycle: 4, delta: 12 }, '90': { active: 30, open: 180, done: 3600, cycle: 4.5, delta: 5 },};
const opened = [40, 45, 50, 48, 60, 55, 70, 65, 80, 75, 90, 85];const closed = [30, 40, 45, 50, 50, 55, 60, 65, 70, 75, 80, 85];const statuses: StatusKey[] = ['completed', 'in-review', 'active', 'pending', 'completed'];
type Row = { id: string; name: string; status: StatusKey; owner: string; updated: string };const helper = createDataTableColumnHelper<Row>();
export type DashboardShellBlockProps = { lang?: Lang; /** Shows the loading state of every region at once, as on first load. */ loading?: boolean; defaultRange?: Range;};
/** A dashboard page: range control, four KPIs, a trend chart and the latest records. */export function DashboardShellBlock({ lang = 'tr', loading = false, defaultRange = '30' }: DashboardShellBlockProps) { const t = copy[lang]; const [range, setRange] = useState<Range>(defaultRange); const number = new Intl.NumberFormat(lang === 'tr' ? 'tr-TR' : 'en-GB'); const f = figures[range]; // Turkish puts the percent sign first. const percent = (value: number) => (lang === 'tr' ? `%${value}` : `${value}%`);
const series = useMemo<ChartSeries[]>( () => [ { id: 'opened', label: t.opened, data: opened.map((y, index) => ({ x: t.week(index + 1), y })) }, { id: 'closed', label: t.closed, data: closed.map((y, index) => ({ x: t.week(index + 1), y })) }, ], [t], );
const rows = useMemo<Row[]>(() => { const date = new Intl.DateTimeFormat(lang === 'tr' ? 'tr-TR' : 'en-GB', { day: 'numeric', month: 'short' }); return statuses.map((status, index) => ({ id: `r${index + 1}`, name: t.record(index + 1), status, owner: t.team((index % 3) + 1), updated: date.format(new Date(2026, 9, 28 - index)) })); }, [t, lang]);
const columns = useMemo( () => [ helper.accessor('name', { header: t.name, meta: { rowHeader: true } }), helper.accessor('status', { header: t.status, enableSorting: false, cell: (context) => <StatusBadge status={context.getValue()} size="sm" /> }), helper.accessor('owner', { header: t.owner }), helper.accessor('updated', { header: t.updated }), ], [t], );
return ( <AppShellTemplate lang={lang} current="home" title={t.title} description={t.description} loading={loading} actions={ <> <SegmentedControl label={t.range} size="sm" value={range} onValueChange={(value) => setRange(value as Range)} items={(['7', '30', '90'] as const).map((value) => ({ value, label: t.ranges[value] }))} /> <Button size="sm" startIcon={<Icon name="file" />}> {t.report} </Button> </> } > {/* Figures change by remount (110 ms cross-fade); amounts never count up. */} <Grid columns={{ base: 2, md: 4 }} gap="lg"> <StatCard key={`active-${range}`} label={t.active} value={number.format(f.active)} loading={loading} /> <StatCard key={`open-${range}`} label={t.open} value={number.format(f.open)} loading={loading} /> <StatCard key={`done-${range}`} label={t.done} value={number.format(f.done)} delta={{ value: percent(f.delta), direction: 'up' }} period={t.period} loading={loading} /> <StatCard key={`cycle-${range}`} label={t.cycle} value={t.days(number.format(f.cycle))} delta={{ value: percent(5), direction: 'down', sentiment: 'positive' }} period={t.period} loading={loading} /> </Grid> <Chart kind="area" title={t.chart} description={t.chartDescription} series={series} height="md" loading={loading} formatValue={(value) => number.format(value)} /> <DataTable caption={t.recent} columns={columns} data={rows} getRowId={(row) => row.id} status={loading ? 'loading' : 'ready'} density="compact" /> </AppShellTemplate> );}- AppShell şablonu: gezinme ve üst çubuk.
- PageHeader:
h1“Genel bakış”, SegmentedControl tarih aralığı ve “Rapor oluştur”. - Dört StatCard: 1024 px altında iki, üstünde dört sütun.
- Chart alan grafiği: açılan ve kapanan görevler, 12 hafta.
- DataTable “Son kayıtlar”: durum StatusBadge ile.
Erişilebilirlik
Bölüm başlığı “Erişilebilirlik”- Sayfada tek
h1vardır; grafik ve tablo kendi başlık vecaptionöğeleriyle adlandırılır. - Tarih aralığı
radiogroupolarak okunur; tek sekme durağı, ok tuşlarıyla seçilir. - Grafik
role="img"ve özet metniyle okunur; “Veriyi tablo olarak göster” yedeği vardır. - Yüklenirken bölgeler
aria-busytaşır; sayfa başlığı yerinde kalır. - Rakam değişimi 110 ms çapraz solmadır; tutarlar sayarak artmaz.
Kullanılan bileşenler
Bölüm başlığı “Kullanılan bileşenler”StatCard, Chart, DataTable, SegmentedControl, StatusBadge, Grid, PageHeader. Kalıplar: Yükleme durumları, Grafik galerisi.