# Tasarım Kimliği (@tecof/app-ui)

> @tecof/app-ui ile Tecof panel görünümünde uygulama arayüzü: kurulum, globals.css katman sırası, TecofAppProvider, AppShell ve bileşenler, durum sözlüğü, ekran düzenleri, metin tonu ve yasaklar.
>
> Kaynak: https://tecof-app-dev.vercel.app/app-ui · Bölüm: Uygulama geliştirme · Son güncelleme: 2018-10-20

Panelde iframe içinde açılan her Tecof uygulaması **aynı görünür ve aynı davranır**. `@tecof/app-ui` bu ortak kimliği verir: React Suite 6 + Tailwind CSS 4 + lucide-react üzerine panelle aynı görsel dil — Tecof lime imza rengi, zinc nötrler, Geist, `rounded-2xl` kartlar, panelden gelen açık/koyu tema ve tr/en metinler.

| Giriş | Ortam | İçerik |
| :--- | :--- | :--- |
| `@tecof/app-ui` | İstemci (`"use client"` derlemede eklenir) | `TecofAppProvider`, kabuk ve yerleşim, durum, tablo, form bileşenleri, `useAppToast`, `useAppConfirm` |
| `@tecof/app-ui/schema` | **Sunucu-güvenli** (React/DOM yok) | `createModel`, `v` kuralları, `validateOnServer`, VKN/TCKN/IBAN/telefon doğrulayıcıları, durum sözlüğü, biçimlendiriciler |
| `@tecof/app-ui/styles.css` | Tailwind 4 CSS | Tema belirteçleri, React Suite → Tecof değişkenleri |

Peer bağımlılıklar: `react ^19`, `react-dom ^19`, `rsuite ^6.2`, `lucide-react`, `@tecof/app-bridge ^0.1`. `tecof app init` şablonları bu kurulumla gelir; aşağıdaki adımlar mevcut bir uygulamayı taşırken ya da elle kurarken gereklidir.

## Kurulum

```bash npm2yarn
npm install @tecof/app-ui @tecof/app-bridge @tecof/app-sdk rsuite lucide-react geist
npm install -D tailwindcss @tailwindcss/postcss
```

```js title="postcss.config.mjs"
export default { plugins: { "@tailwindcss/postcss": {} } };
```

`app/globals.css` **tam olarak** şu dört satırdır; katman sırası önemlidir ve başka global CSS eklenmez:

```css title="app/globals.css"
@layer theme, base, rsuite, components, utilities;
@import "tailwindcss";
@import "rsuite/dist/rsuite-no-reset.min.css" layer(rsuite);
@import "@tecof/app-ui/styles.css";
```

```tsx title="app/layout.tsx"
import type { Metadata } from "next";
import { GeistMono } from "geist/font/mono";
import { GeistSans } from "geist/font/sans";
import { Providers } from "./providers";
import "./globals.css";

export const metadata: Metadata = { title: "Uygulamam", robots: { index: false, follow: false } };

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="tr" data-theme="light" suppressHydrationWarning className={`${GeistSans.variable} ${GeistMono.variable}`}>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
```

```tsx title="app/providers.tsx"
"use client";
import { TecofAppProvider } from "@tecof/app-ui";

export function Providers({ children }: { children: React.ReactNode }) {
  // App Bridge'i kurar, tema/dili panelden alır, <html data-theme class="dark" lang> ayarlar,
  // toast ve onay köprüsünü sağlar.
  return <TecofAppProvider>{children}</TecofAppProvider>;
}
```

Panel dışında geliştirmek için `allowStandalone` (köprü `null`, toast/onay React Suite ile çalışır) ya da geçici olarak `bridge="off" theme="dark" locale="en"` verin. Kalıcı kodda `bridge="off"` bırakmayın.

## Kabuk

```tsx title="app/app-shell.tsx"
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { AppShell, StatusBadge, useTecofApp } from "@tecof/app-ui";
import { FileText, LayoutDashboard, Settings } from "lucide-react";

export function Shell({ children }: { children: React.ReactNode }) {
  const { context, locale } = useTecofApp();
  const en = locale === "en";
  return (
    <AppShell
      title={context?.app.name ?? "Uygulama"}
      subtitle={context?.merchant.name}
      icon={<FileText />}
      status={<StatusBadge status="connected" />}
      linkAs={Link}
      activeHref={usePathname()}
      tabs={[
        { href: "/", label: en ? "Overview" : "Genel bakış", icon: <LayoutDashboard /> },
        { href: "/settings", label: en ? "Settings" : "Ayarlar", icon: <Settings /> },
      ]}
    >
      {children}
    </AppShell>
  );
}
```

`AppShell`’e `linkAs={Link}` bir fonksiyon geçtiği için kabuğu istemci bileşeninde kullanın. Simge prop’ları **eleman** alır (`icon={<Receipt />}`), böylece sunucu bileşenlerinden de verilebilir.

## Bileşenler

| Bileşen | Kullanım |
| :--- | :--- |
| `PageHeader` | Sayfa başlığı ve tek ana eylem; `eyebrow` yazıldığı gibi gösterilir (`eyebrowCase="upper"` isteğe bağlı) |
| `Section` | Kart; form ve tablo için `padded={false}`, tehlikeli işlem için `tone="danger"` |
| `StatGrid` + `StatCard` | 2–4 metrik; `loading` destekler |
| `DataTable` | React Suite tablosu; 640 px altında kart görünümü (`mobile: "title" \| "badge" \| "meta" \| "hidden"`), uzun metin için `truncate: true`, boş liste için `empty` |
| `StatusBadge` | Ortak durum sözlüğünden ton ve tr/en etiket |
| `Callout` | Bilgi/uyarı/hata mesajı (`tone`) |
| `EmptyState` | Boş durum: ne olacağını ve ilk adımı söyler |
| `KeyValueList`, `CopyValue`, `LogList` | Ayrıntı ekranları |
| `ConfirmButton` | Panelde App Bridge onayıyla çalışan tehlikeli eylem düğmesi |
| `useAppToast()`, `useAppConfirm()` | Panel toast’ı ve onayı |

Düğmeler React Suite `Button`’dır: ekranda **tek** ana eylem `appearance="primary"` (lime), diğerleri `default` ya da `subtle`. İkonlar yalnız `lucide-react`, düğmede `size-4`.

### Durum sözlüğü

`<StatusBadge status="…" />` tek sözlükten ton ve etiket alır; sözlükte olmayan durum için `tone` + `label` verin, yeni renk uydurmayın.

| Grup | Durumlar |
| :--- | :--- |
| Genel | `success` · `warning` · `error` · `info` · `neutral` · `pending` |
| Fatura | `creating` · `draft` · `issued` · `signed` · `failed` · `skipped` · `needs-cancellation` · `deleted` · `cancelled` |
| Gönderim | `queued` · `processing` · `sending` · `sent` · `delivered` · `undelivered` · `rejected` |
| Kargo | `label-created` · `shipped` · `in-transit` · `out-for-delivery` · `returned` |
| Bağlantı / abonelik | `connected` · `disconnected` · `not-configured` · `active` · `inactive` · `trialing` · `past_due` · `expired` |

Sunucuda aynı sözlük `statusTone` / `statusLabel` ile (`@tecof/app-ui/schema`) okunur.

## Ekran düzenleri

| Ekran | Düzen |
| :--- | :--- |
| Genel bakış (`/`) | `PageHeader` (+ tek ana eylem) → yalnız eylem gerektiren `Callout` (ayar eksik, test modu, plan) → `StatGrid` → son kayıtlar `Section` + `DataTable` (5–10 satır, “Tümünü gör”) → hiç kayıt yoksa `EmptyState` |
| Ayarlar (`/settings`) | `PageHeader` → `AppForm` içinde konu başına `Section padded={false}` → `FormActions` (mobilde alta yapışık) → ayrı `Section tone="danger"` (bağlantıyı kes, `ConfirmButton`) |
| Kayıtlar (`/invoices`, `/logs`…) | `PageHeader` (+ filtre) → `Section padded={false}` + `DataTable` (`onRowClick` → `Drawer` ya da detay sayfası) → `Pagination`; detayda `KeyValueList` + `LogList` |
| Aksiyon penceresi | Kabuksuz: başlık **yalnız** `bridge.setTitle(...)` ile panel çekmecesine gider (sayfada başlık satırı ve kendi X’i yok); kısa bağlam satırı + `Section` + altta yapışık Vazgeç/Tamamla; bitince `bridge.close({ refresh: true, message, kind })`. Fatura uygulamalarında `@tecof/invoice-kit/ui` `ActionFrame` |

Durumlar: yükleniyor → `Placeholder.Paragraph` / `StatCard loading` / `DataTable loading`; hata → `Callout tone="error"` + “Tekrar dene”; boş → `EmptyState`. Tarih ve para için `formatDateTime` (Europe/Istanbul, hidrasyon güvenli) ve `formatMoney` (`@tecof/app-ui/schema`).

## Metin tonu

Türkçe öncelikli, `en` karşılığıyla. Kısa, “siz” dili, cümle düzeni (“Ayarları Kaydet” değil “Kaydet”). Eylemler fiille (“Bağlan”, “Tekrar dene”, “Fatura oluştur”). Hata metni ne olduğunu ve ne yapılacağını söyler: “Sağlayıcı kimlik bilgilerini reddetti. API anahtarını kontrol edip tekrar deneyin.” Ünlem, teknik kod ve stack izi yok. Dil `useTecofApp().locale` (`tr` | `en`); sunucuda `resolveLocale(session.locale)`.

## Yapmayın

- Marka/ürün adına `uppercase` vermeyin (Türkçe büyük harf “KolayBi” → “KOLAYBİ”).
- Kendi renk paleti, CSS değişken seti, ayrı `.css` dosyası, inline style ile tema, başka UI kütüphanesi (shadcn, MUI…), başka ikon seti ya da emoji.
- Ham `<input>` / `<select>` / `<button>` ile form; `prefers-color-scheme` ile tema; `@custom-variant dark` yeniden tanımı. Koyu tema panelden gelir, `dark:` sınıflarıyla kullanılır.
- Sunucu kodunda `@tecof/app-ui` (istemci girişi) — sunucuda yalnız `@tecof/app-ui/schema`.
- Sır değerini, oturum token’ını ya da müşteri kişisel verisini ekrana veya günlüğe basmak.
- Kit eksiğini kalıcı geçici çözümle kapatmak: önce kitte karşılığını arayın (`truncate`, `SubmitButton`, `onCheckFail`).

Formlar ve tek şemalı doğrulama: [Formlar ve şema](/app-forms). Görsel doğrulama için uygulamayı panel dışında `allowStandalone` ile açıp masaüstü (1280) ve mobil (390) genişlikte, açık ve koyu temada kontrol edin.
