İçeriğe geç
BigBang

Sık açılanlar

    ↑↓Gezin↵AçEscKapat
    EN

    Şablonlar

    Dekont ve döküm

    PrintShell üzerinde tek kaydın özeti veya bir dönemin kayıtları; bilgiler tanım listesinde, rakamlar tabloda.

    Bu blok PrintShell içinde iki belge türü kurar: tek kaydın kalemleriyle özeti (variant="receipt") ve bir dönemin kayıt dökümü (variant="statement").

    Sayfa başlığındaki marka ve mod seçimi önizlemeyi de değiştirir. “Yazdır” düğmesi tarayıcının yazdırma penceresini açar.

    Canlı örnek, StorybookStorybook'ta aç
    Canlı örnek, StorybookStorybook'ta aç
    Canlı örnek, StorybookStorybook'ta aç
    Terminal window
    pnpm add @bigbang/ui
    Veri Tür Not
    variant 'receipt' | 'statement' Kalemli özet veya dönem dökümü.
    Kalemler { quantity, unit }[] Tutarlar Intl.NumberFormat ile, iki ondalıkla yazılır.
    Referans string Yalnız son dört karakter görünür; maskeleme sunucuda yapılır.
    import { Button, DescriptionList, Heading, Icon, Stack, StatusBadge, Table, Text } from '@bigbang/ui';
    import type { StatusKey } from '@bigbang/ui';
    import type { Lang } from '../../patterns/lang';
    import { PrintShell } from '../../templates/print-shell/PrintShell';
    const copy = {
    tr: {
    receipt: 'İşlem özeti',
    statement: 'Dönem dökümü',
    number: 'Belge no',
    date: 'Tarih',
    print: 'Yazdır',
    pdf: 'PDF olarak indir',
    details: 'Ayrıntılar',
    type: 'Kayıt türü',
    typeValue: 'Hizmet kaydı',
    status: 'Durum',
    owner: 'Oluşturan',
    ownerValue: 'Kullanıcı A',
    reference: 'Referans',
    lines: 'Kalemler',
    description: 'Açıklama',
    quantity: 'Adet',
    unit: 'Birim tutar',
    total: 'Toplam',
    grandTotal: 'Genel toplam',
    items: ['Hizmet A', 'Hizmet B', 'Hizmet C'],
    period: 'Dönem',
    periodValue: '1–31 Ekim 2026',
    records: 'Dönem kayıtları',
    record: 'Kayıt',
    footer: 'Bu belge elektronik olarak oluşturulmuştur. Doğrulama kodu: 7K2P-9QX4',
    },
    en: {
    receipt: 'Record summary',
    statement: 'Period statement',
    number: 'Document no.',
    date: 'Date',
    print: 'Print',
    pdf: 'Download as PDF',
    details: 'Details',
    type: 'Record type',
    typeValue: 'Service record',
    status: 'Status',
    owner: 'Created by',
    ownerValue: 'User A',
    reference: 'Reference',
    lines: 'Line items',
    description: 'Description',
    quantity: 'Quantity',
    unit: 'Unit amount',
    total: 'Total',
    grandTotal: 'Grand total',
    items: ['Service A', 'Service B', 'Service C'],
    period: 'Period',
    periodValue: '1–31 October 2026',
    records: 'Records in the period',
    record: 'Record',
    footer: 'This document was created electronically. Verification code: 7K2P-9QX4',
    },
    };
    const lineItems = [
    { quantity: 2, unit: 500 },
    { quantity: 1, unit: 1000 },
    { quantity: 4, unit: 250 },
    ];
    const statementRows: { day: number; status: StatusKey; amount: number }[] = [
    { day: 2, status: 'completed', amount: 1000 },
    { day: 6, status: 'completed', amount: 250 },
    { day: 11, status: 'cancelled', amount: 500 },
    { day: 17, status: 'completed', amount: 750 },
    { day: 23, status: 'pending', amount: 1200 },
    { day: 29, status: 'completed', amount: 300 },
    ];
    export type ReceiptBlockProps = {
    lang?: Lang;
    /** `receipt` for one record with line items, `statement` for the records of a period. */
    variant?: 'receipt' | 'statement';
    };
    /** A printable document on PrintShell: facts as a description list, figures in a table, a masked reference. */
    export function ReceiptBlock({ lang = 'tr', variant = 'receipt' }: ReceiptBlockProps) {
    const t = copy[lang];
    const locale = lang === 'tr' ? 'tr-TR' : 'en-GB';
    const formatAmount = (value: number) => new Intl.NumberFormat(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(value);
    const date = new Intl.DateTimeFormat(locale, { dateStyle: 'medium' });
    const isReceipt = variant === 'receipt';
    const total = isReceipt ? lineItems.reduce((sum, item) => sum + item.quantity * item.unit, 0) : statementRows.filter((row) => row.status !== 'cancelled').reduce((sum, row) => sum + row.amount, 0);
    return (
    <PrintShell
    lang={lang}
    title={isReceipt ? t.receipt : t.statement}
    meta={`${t.number} BB-2026-000123 · ${t.date} ${date.format(new Date(2026, 9, 28))}`}
    footer={t.footer}
    actions={
    <>
    <Button variant="secondary" size="sm" startIcon={<Icon name="file" />} onClick={() => window.print()}>
    {t.print}
    </Button>
    <Button variant="tertiary" size="sm" startIcon={<Icon name="download" />}>
    {t.pdf}
    </Button>
    </>
    }
    >
    <Stack gap="2xl">
    <section aria-labelledby={`receipt-details-${lang}`}>
    <Stack gap="md">
    <Heading level={2} size="sm" id={`receipt-details-${lang}`}>
    {t.details}
    </Heading>
    <DescriptionList
    layout="grid"
    columns={2}
    items={
    isReceipt
    ? [
    { term: t.type, details: t.typeValue },
    { term: t.status, details: <StatusBadge status="completed" size="sm" /> },
    { term: t.owner, details: t.ownerValue },
    { term: t.reference, details: <Text numeric>•••• 1234</Text> },
    ]
    : [
    { term: t.period, details: t.periodValue },
    { term: t.owner, details: t.ownerValue },
    { term: t.reference, details: <Text numeric>•••• 1234</Text> },
    { term: t.records, details: statementRows.length },
    ]
    }
    />
    </Stack>
    </section>
    {/* The table's scroll region already carries this name, so the section stays unnamed to keep landmarks unique. */}
    <section>
    <Stack gap="md">
    <Heading level={2} size="sm" id={`receipt-lines-${lang}`}>
    {isReceipt ? t.lines : t.records}
    </Heading>
    {isReceipt ? (
    <Table caption={t.lines} captionHidden density="compact">
    <Table.Header>
    <Table.Row>
    <Table.HeaderCell>{t.description}</Table.HeaderCell>
    <Table.HeaderCell numeric>{t.quantity}</Table.HeaderCell>
    <Table.HeaderCell numeric>{t.unit}</Table.HeaderCell>
    <Table.HeaderCell numeric>{t.total}</Table.HeaderCell>
    </Table.Row>
    </Table.Header>
    <Table.Body>
    {lineItems.map((item, index) => (
    <Table.Row key={t.items[index]}>
    <Table.RowHeader>{t.items[index]}</Table.RowHeader>
    <Table.Cell numeric>{item.quantity}</Table.Cell>
    <Table.Cell numeric>{formatAmount(item.unit)}</Table.Cell>
    <Table.Cell numeric>{formatAmount(item.quantity * item.unit)}</Table.Cell>
    </Table.Row>
    ))}
    </Table.Body>
    <Table.Footer>
    <Table.Row>
    <Table.RowHeader colSpan={3}>{t.grandTotal}</Table.RowHeader>
    <Table.Cell numeric>{formatAmount(total)}</Table.Cell>
    </Table.Row>
    </Table.Footer>
    </Table>
    ) : (
    <Table caption={t.records} captionHidden density="compact">
    <Table.Header>
    <Table.Row>
    <Table.HeaderCell>{t.date}</Table.HeaderCell>
    <Table.HeaderCell>{t.description}</Table.HeaderCell>
    <Table.HeaderCell>{t.status}</Table.HeaderCell>
    <Table.HeaderCell numeric>{t.total}</Table.HeaderCell>
    </Table.Row>
    </Table.Header>
    <Table.Body>
    {statementRows.map((row, index) => (
    <Table.Row key={row.day}>
    <Table.Cell>{date.format(new Date(2026, 9, row.day))}</Table.Cell>
    <Table.RowHeader>{`${t.record} ${index + 1}`}</Table.RowHeader>
    <Table.Cell>
    <StatusBadge status={row.status} size="sm" />
    </Table.Cell>
    <Table.Cell numeric>{formatAmount(row.amount)}</Table.Cell>
    </Table.Row>
    ))}
    </Table.Body>
    <Table.Footer>
    <Table.Row>
    <Table.RowHeader colSpan={3}>{t.grandTotal}</Table.RowHeader>
    <Table.Cell numeric>{formatAmount(total)}</Table.Cell>
    </Table.Row>
    </Table.Footer>
    </Table>
    )}
    </Stack>
    </section>
    </Stack>
    </PrintShell>
    );
    }
    1. “Belge işlemleri” araç çubuğu: Yazdır ve PDF olarak indir (kâğıtta gizli).
    2. Üst bilgi: marka, h1 başlık, belge no ve tarih.
    3. “Ayrıntılar” bölümü: DescriptionList ızgara düzeninde; durum StatusBadge ile.
    4. “Kalemler” veya “Dönem kayıtları” bölümü: Table, sayısal sütunlar sağa hizalı, toplam tfoot içinde.
    5. Alt bilgi: elektronik belge notu ve doğrulama kodu.
    • Her bölüm h2 başlığıyla adlandırılmış bir section’dır.
    • Tablolar gizli başlıkla (caption) adlandırılır; açıklama hücreleri satır başlığıdır (th scope="row").
    • Sayısal hücreler tabular-nums ile hizalanır; tutarlar sayarak değişmez.
    • İptal edilen kayıt toplamın dışında kalır ve durumu metinle okunur.

    DescriptionList, Table, StatusBadge, Button, Heading, Text. Kalıplar: Veri dışa aktarma.