LivaDocsv1.1.2

Veri Alışverişi (DataExchange)

Şirket genelinde ortak olan içe/dışa aktarma standardının önyüz tarafı. Grid'e bir process key verirsiniz; kolon şemasını, zorunlu alanları, lookup eşlemelerini ve doğrulama kurallarını sunucu bildirir. Livalib bu sözleşmeyi okuyup şablon indirme, doğrulama, kolon eşleştirme, önizleme ve içe aktarma adımlarını kendisi yürütür — sayfada elle Excel ayrıştırma kodu kalmaz. Kullanıcının Excel'i şablondan farklı olsa bile kolonlar sihirbazda sistem alanlarına bağlanır; kurulan eşleme kullanıcıya özel bir profil olarak saklanabilir.

import {
  LivaGridConfigProvider,
  LivaImportWizard,
  useDataExchange,
  useContract,
  createColumnMappingProfileApiStorage,
  type DataExchangeContract,
  type ColumnMappingProfileStorage,
} from "@dataliva/livalib";

Kurulum — LivaGridConfigProvider

Uygulama kökünde bir kez sarmalayın. Modülün tüm HTTP çağrıları buradaki yapılandırmayı kullanır: token Authorization: Bearer …, lang ise Accept-Language başlığına yazılır.

<LivaGridConfigProvider
  dataExchange={{
    baseUrl: `${API_URL}/data-exchange`,
    showDiagnostics: isSysAdmin,
    mappingProfilesUrl: `${API_URL}/UserImportProfile`,
  }}
  token={() => getToken()}
  lang="TR"
>
  <App />
</LivaGridConfigProvider>
AlanTipAçıklama
dataExchange.baseUrlstringUç nokta kökü. Varsayılan /api/data-exchange.
dataExchange.endpointsResolver(processKey) => DataExchangeEndpointsRota şeması standarda uymuyorsa altı uç noktayı elle üretin.
dataExchange.headerPolicy'tech-suffix' | 'header-only'Şablon başlıklarında teknik alan adının gösterilip gösterilmeyeceği.
dataExchange.showDiagnosticsbooleanSihirbazda teknik bilgileri gösterir (aşağıda Teknik bilgiler). Varsayılan false.
dataExchange.mappingProfilesUrlstringKullanıcı bazlı eşleştirme profillerinin REST tabanı (aşağıda Eşleştirme profilleri).
dataExchange.mappingProfileStorageColumnMappingProfileStorageREST yerine özel profil deposu; mappingProfilesUrl'den önceliklidir.
tokenstring | (() => string | undefined)Fonksiyon verin; token yenilendiğinde provider'ı yeniden render etmeye gerek kalmaz.
langstringAccept-Language başlığı (ör. "TR").
fetchtypeof fetchTest veya interceptor için fetch değişimi.

mappingProfilesUrl ve mappingProfileStorage ikisi de verilmezse sihirbazdaki profil bölümü hiç görünmez; kolon eşleştirme yine çalışır.

baseUrl verildiğinde uç noktalar şu kalıptan türetilir:

Uç noktaRota
definition{base}/{processKey}/definition
template{base}/{processKey}/template
export{base}/{processKey}/export
import{base}/{processKey}/import
validate{base}/{processKey}/validate
status{base}/{processKey}/status

validate ve import rotalarının sonundaki ek yollar (/validate-rows, /validate-source, /import-rows, /import-source) bu iki taban üzerinden otomatik türetilir. Adaptör kataloğu ise {base}/adapters?capability=read adresinden okunur.

İstekler ve route gövdesi

routeParams URL'ye eklenmez; her istekte gövdedeki route alanında taşınır. Yıl/şirket gibi kapsam bilgisi ya da uzun bir kimlik listesi (ör. ccntId=a,b,c,…) query string'e konduğunda IIS maxQueryString sınırını aşar ve sunucu API'ye hiç ulaşmadan 404 döner — bu yüzden gövde kullanılır.

Çağrıİstek
definitionGET, gövdesiz
templatePOST · { withData, route }
exportPOST · { ...payload, route }
validate / importPOST · multipart: file + route (JSON metni olarak form alanı)
validate-rows / import-rowsPOST · { rows, route }
validate-source / import-sourcePOST · { sourceKind, sourceOptions, route }
adaptersGET {base}/adapters?capability=read|write

route bir Record<string, string> nesnesidir: değerler metne çevrilir, undefined, null ve "" olanlar atılır; geriye bir şey kalmazsa route alanı hiç gönderilmez. JSON gövdeli isteklere Content-Type: application/json eklenir, multipart istekte başlığı tarayıcı belirler. Başarısız (2xx dışı) her yanıt "POST {url} → 404" biçiminde mesajı olan bir Error fırlatır; sihirbaz bu metni üstteki hata bandında gösterir.

Grid'e bağlama — bildirimsel yol

LivaDataGrid ve LivaTreeList, importExportSettings.processKey verildiğinde içe aktarma sihirbazını kendiliğinden monte eder. Ayrıca araç çubuğundaki menü de sözleşmeye göre değişir: Boş Şablon İndir ve Verili Şablon İndir öğeleri çıkar, eski SQL Export / SQL Import öğeleri gizlenir.

<LivaDataGrid
  {...getGridProps()}
  keyExpr="id"
  columns={kolonlar}
  dataSource={ds}
  importExportSettings={{
    enabled: true,
    processKey: "expense-definition",
    routeParams: { budgetYear, companyId },
    wizardTitle: "Masraf Tanımı İçe Aktarma",
  }}
/>
AlanTipAçıklama
enabledbooleanAraç çubuğundaki içe/dışa aktarma butonunu açar.
processKeystringSözleşme anahtarı. Verildiğinde sihirbaz devreye girer.
baseUrlstringYalnızca bu grid için kök adres değişimi.
endpointsPartial<DataExchangeEndpoints>Tek tek uç nokta ezme.
headerPolicy'tech-suffix' | 'header-only'Başlık politikası ezme.
contractDataExchangeContractSözleşme elde varsa definition isteği atlanır.
autoProvisionRecord<string, boolean>Eksik kayıtların otomatik oluşturulmasını alan bazında aç/kapat.
routeParamsRecord<string, string | number | boolean | null | undefined>Tüm isteklerin gövdesinde route olarak gönderilir.
configPartial<LivaGridConfig>Provider yapılandırmasını bu grid için ezer.
wizardTitlestringSihirbaz başlığı.
closeOnCompletebooleanVarsayılan false — sihirbaz rapor adımında açık kalır.
onImportCompleted(report: DataExchangeReport) => voidİçe aktarma raporu döndüğünde, grid refresh() edildikten sonra çağrılır.

routeParams'ı eksik bırakmak en sık yapılan hatadır: yıl/şirket gibi bağlam sunucuya gitmezse şablon boş, doğrulama ise "kayıt bulunamadı" hatalarıyla döner.

Aktarım bitince grid kendi refresh()'ini çağırır; bu yalnızca bağlı dataSource'u yeniden okur. Grid'e yerel bir dizi verdiyseniz yeni kayıtlar görünmez — veriyi onImportCompleted içinde kendiniz yeniden yükleyin. LivaTreeList aynı ayar tipini kullanır, ancak sihirbazı aktarım bitince closeOnComplete'e bakmadan her zaman kapatır.

config sığ birleştirilir: config.dataExchange verdiğinizde uç nokta çözümünde provider'daki dataExchange nesnesinin tamamının yerine geçer. Bu durumda baseUrl'i ya config.dataExchange içinde ya da grid'in baseUrl alanında yeniden verin; aksi halde istekler varsayılan /api/data-exchange köküne gider.

Eski kancalar (escape hatch)

Standart sihirbazdan çıkmak için onImportExcel verin — bu alan doluyken sihirbaz devre dışı kalır ve klasik dosya seçici açılır. Aynı şekilde onExportExcel, onDownloadTemplate, onImportSettings ve onExportSettings kendi akışınızı bağlamak içindir. Yeni sayfalarda bunlara ihtiyaç olmamalıdır.

İçe aktarma sihirbazı — LivaImportWizard

Grid dışında (ör. bir sayfa butonundan) elle de monte edilebilir:

const [acik, setAcik] = useState(false);

<LivaImportWizard
  processKey="expense-definition"
  routeParams={{ budgetYear }}
  visible={acik}
  onHiding={() => setAcik(false)}
  onCompleted={(rapor) => {
    notifications.show({ title: "İçe aktarma", message: `${rapor.insertedCount} kayıt eklendi` });
    gridRef.current?.instance().refresh();
  }}
  title="Masraf Tanımı İçe Aktarma"
  translate={t}
  showDiagnostics={isSysAdmin}
/>
PropTipVarsayılan
processKeystring— (zorunlu)
visibleboolean— (zorunlu)
onHiding() => void— (zorunlu)
baseUrlstring—
endpointsPartial<DataExchangeEndpoints>—
contractDataExchangeContract— (verilirse definition isteği atlanır)
routeParamsRouteParams—
configPartial<LivaGridConfig>—
onCompleted(report: DataExchangeReport) => void—
titlestring"Veri İçe Aktarma" (showDiagnostics açıkken "Veri İçe Aktarma — {processKey}")
translate(key: string) => string—
showDiagnosticsbooleanconfig.dataExchange.showDiagnostics → provider ayarı → false

onCompleted, import-rows isteği bir rapor döndürdüğü anda çağrılır — rapor satır hataları içerse (errorCount > 0) bile. İstek tümden başarısız olursa çağrılmaz; hata sihirbazın üstünde gösterilir.

Adımlar

  1. Kaynak — adaptör kataloğu capability=read ile çekilir. Katalogda tek adaptör varsa kendiliğinden seçilir. Adaptörün transport değeri akışı belirler: multipart dosya yükler, source ise adaptörün kendi alanlarını (fields) doldurtup sunucudan okur. Önizle düğmesi, adaptörün zorunlu alanları dolunca açılır.
  2. Önizleme — multipart için dosya sunucuya validate ile gönderilirken aynı anda tarayıcıda da okunur; source için validate-source çağrılır. Sözleşme validate yanıtındaki contract alanından alınır. İlk görünüm kaynak Excel'in kendisidir (Görünüm: Kaynak Excel): salt okunurdur, sunucunun ilk doğrulamasındaki hata sayısı yalnızca bilgi olarak gösterilir. İleri gitmenin tek yolu Kolon Eşleştirme düğmesidir.
  3. Kolon Eşleştirme — Excel kolonları sözleşmedeki alanlara bağlanır, Eşlemeyi Uygula ve Devam ile eşlenmiş satırlar validate-rows'a gider (ayrıntı aşağıda Kolon eşleştirme). Hata yoksa doğrudan Onay'a geçilir. Hata dönerse sihirbaz önizlemeye geri döner; bu kez görünüm Eşlenmiş veridir, hücreler düzenlenebilir, satırlar silinebilir ve hatalı hücreler kırmızı işaretlenir. Düzenlemeden sonra Kontrol Et satırları yeniden validate-rows ile doğrular; hata ya da kontrol edilmemiş düzenleme kaldıkça Devam kapalıdır.
  4. Onay — hata kalmadıysa açılır. Planlanan ekleme/güncelleme sayıları (plannedInsertCount, plannedUpdateCount) ve toplam satır gösterilir. Başarılı olursa bu pencereyi otomatik kapat kutusu işaretlenirse errorCount === 0 olan aktarımdan sonra sihirbaz kapanır.
  5. Rapor — DataExchangeReport özeti: eklenen, güncellenen, hatalı ve işlenen satır sayıları, aktarım öncesi/sonrası kayıt sayısı (recordsBefore / recordsAfter) ve varsa satır hataları tablosu. closeOnComplete verilmedikçe sihirbaz bu adımda açık kalır.

İçe aktarma, kaynak adaptörü ne olursa olsun her zaman import-rows üzerinden gönderilir: sunucuya giden, eşlenmiş ve önizlemede düzeltilmiş satırlardır. Sihirbaz import ve import-source uçlarını kullanmaz; bunlar useDataExchange üzerinden erişilebilir kalır.

validate-rows çağrıları 5000 satırlık parçalar halinde yapılır; parçaların hataları birleştirilirken rowNumber değerleri kaydırılır, böylece hata satır numaraları dosyanın tamamına göre kalır.

Teknik bilgiler — showDiagnostics

Açıkken sihirbaz başlığına process key eklenir (title verilmediyse) ve Onay adımında bir Kaynak kartı çıkar: kullanılan adaptör (dosya adı ya da adaptörün görünen adı), sözleşmenin eşleşme anahtarı (upsertKey) ve süreç anahtarı. Son kullanıcıyı ilgilendirmeyen bu bilgileri yalnızca yöneticilere açmak için provider'da bir kez verin:

<LivaGridConfigProvider dataExchange={{ baseUrl, showDiagnostics: isSysAdmin }}>

Öncelik sırası: showDiagnostics prop'u → config.dataExchange.showDiagnostics → provider'daki dataExchange.showDiagnostics → false. Grid'in kendiliğinden monte ettiği sihirbaz bu prop'u almaz; orada provider ayarı ya da importExportSettings.config.dataExchange.showDiagnostics geçerlidir.

Kolon eşleştirme

Sihirbazdaki her içe aktarma bu adımdan geçer. Kullanıcının Excel'i şablonla birebir aynı olmak zorunda değildir: kolonları farklı adlandırılmış, farklı sıralanmış ya da fazladan kolon içeren bir dosya da burada sistem alanlarına bağlanır.

Geliştiricinin ayrıca bir şey sağlaması gerekmez — ekranı sözleşmedeki columns sürer: her kolonun header'ı satır başlığı, required değeri Zorunlu rozeti, name ve dataType alt bilgi olarak gösterilir; satırlar order'a göre dizilir.

Kaynak tablo. multipart adaptörlerde dosyanın ilk çalışma sayfası okunur; ilk satır başlık satırıdır. Başlığı boş kolonlar atlanır, aynı başlık tekrar ediyorsa sonrakiler Başlık (2), Başlık (3) diye ayrılır, tamamen boş satırlar atılır. Dosya boşsa, çalışma sayfası ya da başlık satırı yoksa sihirbaz hata verir; başlık olup veri satırı yoksa Kolon Eşleştirme düğmesi kapalı kalır. source adaptörlerde kaynak kolonları, validate-source yanıtındaki satırların anahtarlarıdır (__ ile başlayanlar hariç).

Her satırda bir seçim kutusu, eşlenen kolondan okunan ilk dolu değeri (Örnek:) ve üstte N / M alan eşleşti sayacı bulunur. Zorunlu olmayan bir alanın eşlemesi temizlenebilir; boş bırakılan alan sunucuya hiç gönderilmez.

Otomatik eşleşme

Adım açıldığında her alan için, henüz kullanılmamış Excel kolonları arasından en yüksek puanlı olan seçilir (bire bir):

PuanKoşul
100Başlıktaki teknik ek kolonun name'ine eşit: Ürün Kodu <ProCode> → ProCode
90Teknik eki atılmış başlık, kolonun header'ına eşit (büyük/küçük harf duyarsız)
80Teknik eki atılmış başlık, kolonun name'ine eşit
70Normalize edilmiş başlık, normalize edilmiş header'a eşit
60Normalize edilmiş başlık, normalize edilmiş name'e eşit

Normalize etmek; Türkçe İ/ı harflerini ve aksanları sadeleştirip harf ve rakam dışındaki her şeyi atmak demektir (Masraf Grubu = masraf_grubu). Teknik ek, başlığın sonunda yalnızca harf, rakam ve _ içerdiğinde tanınır (<ProCode>); headerPolicy: 'tech-suffix' ile indirilen şablonlarda kullanıcı görünen başlığı değiştirse bile kolon doğru alana oturur. Kullanıcının varsayılan eşleştirme profili varsa otomatik eşleşmenin yerine o uygulanır.

Bir alana, başka bir alanın kullandığı Excel kolonu seçilirse iki alanın kolonları yer değiştirir; seçim engellenmez.

Birleştirme (Kod + Ad)

Satırın yanındaki küçük + düğmesi alanı birleştirme kipine alır. Kolonlar sırayla eklenir, ← ile sola taşınır, × ile çıkarılır; iki ve daha fazla kolon olduğunda ayraç seçilir ve sonucun canlı bir örneği gösterilir. Birleştirme her veri tipindeki alanda kullanılabilir.

AyraçDeğer
- (varsayılan)" - "
Boşluk" "
/" / "
_"_"
,", "
Bitişik""
ÖzelEn çok 10 karakter, serbest
  • Boş parçalar atlanır; parçaların hepsi boşsa alan o satırda boş kalır.
  • Parçalar Excel'de görünen metinden okunur; baştaki sıfırlar ve hücre biçimi korunur. Birleşimin sonucu her zaman metindir.
  • Birleşik bir alan, başka bir alanın tek başına kullandığı kolonu da içerebilir (ör. Kod hem kendi alanında hem Kod - Ad içinde).
  • ↶ Birleştirmeyi geri al alanı birleştirmeye başlamadan önceki haline (tek kolon ya da boş) döndürür. Bu bilinmiyorsa — ör. birleşim bir profilden geldiyse ya da kullanıcı adımdan çıkıp geri döndüyse — listedeki ilk kolon kalır.

Tek kolonlu eşlemede string ve guid alanlar da görünen metinden okunur; diğer tiplerde ham hücre değeri gider (tarihler ISO 8601 metnine çevrilir).

Doğrulama ve uygulama

İki durum Eşlemeyi Uygula ve Devam düğmesini kilitler: eşlenmemiş zorunlu bir alan ve aynı Excel kolonunun tek başına birden fazla alana bağlanması. Kural birleşimlere uygulanmaz.

Uygulandığında eşlenmiş satırlar validate-rows ile sunucuda doğrulanır. Hata varsa sihirbaz önizlemeye döner; kullanıcı eşleme adımına geri geldiğinde hatalı alanlar kırmızı işaretlenir (N satırda hata · mesaj), üstte toplam hata sayısı ve Ön izlemede düzelt düğmesi çıkar. Eşleme uygulandıktan sonra değiştirilirse sihirbaz uyarır: yeniden uygulamak, önizlemedeki hücre düzenlemelerini Excel verisinden baştan üretir.

Alan bazlı işaretleme CellError.column üzerinden yapılır: sunucu bu alana sözleşmedeki kolonun name değerini aynen yazmalıdır. column'u boş hatalar yalnızca toplam hata sayısına girer.

Eşleştirme profilleri

Kullanıcı kurduğu eşlemeyi bir ad vererek saklayabilir ve sonraki aktarımlarda tek tıkla uygulayabilir. Profiller kullanıcıya ve process key'e özeldir; kolon sırasını değil Excel başlık metinlerini saklar, bu sayede aynı başlıkları taşıyan başka bir dosyada da çalışır.

Provider'da dataExchange.mappingProfilesUrl (hazır REST deposu) ya da dataExchange.mappingProfileStorage (özel depo) verildiğinde eşleştirme adımının üstünde Eşleştirme profilim çubuğu çıkar. İkisi de grid ya da sihirbaz düzeyinde config.dataExchange ile verilebilir; profil ayarları provider'dakilerle alan alan birleştirilir.

Kullanıcı tarafı

  • Profil seç — seçilen profil eşlemeyi doldurur. Profildeki bir başlık bu dosyada yoksa durum satırında bu dosyada bulunamayan kolon uyarısı çıkar; profil uygulandıktan sonra eşleme değiştirilirse kaydedilmemiş değişiklik var yazar.
  • Kaydet — ad (en çok 200 karakter) ve Varsayılan yap seçeneğiyle kaydeder. Aynı adla kayıtlı profil varsa düğme Güncelle olur; üzerine yazmayı sunucu yapar (bkz. kaydetme gövdesi). Hiç profil yokken Varsayılan yap işaretli gelir; seçili bir profil varsa form onun adı ve varsayılan durumuyla açılır.
  • ★ Varsayılan — seçili profili varsayılan yapar ya da varsayılanlıktan çıkarır. Varsayılan profil, sihirbazda Önizle'ye basıldığında otomatik eşleşmenin yerine kendiliğinden uygulanır. Sihirbaz profil listesini en çok 3 saniye bekler; liste gecikirse otomatik eşleşmeyle devam eder.
  • Sil — onay sorusundan sonra profili siler.

Profil listesi yüklenemezse çubukta Profiller yüklenemedi; eşlemeyi elle yapabilirsiniz. yazar; içe aktarma engellenmez. Kaydetme, silme ve varsayılan değiştirme hataları da çubuğun durum satırında gösterilir.

Profil bir dosyaya nasıl uygulanır: her başlık önce görünen metniyle birebir, bulunamazsa teknik eki atılıp normalize edilerek aranır. Profilde null kaydedilmiş alan bilerek eşlenmemiş kalır. Profilde hiç yer almayan (sözleşmeye sonradan eklenmiş) ya da kolonlarının hiçbiri dosyada bulunamayan alanlar otomatik eşleşmeye bırakılır.

REST sözleşmesi — mappingProfilesUrl

mappingProfilesUrl verildiğinde livalib, createColumnMappingProfileApiStorage ile şu uçları çağırır ({base} sondaki / atılmış taban; {processKey} ve {id} URL kodlanır):

İşlemİstekBeklenen yanıt
ListeleGET {base}/{processKey}200 + profil dizisi. 204 ya da dizi olmayan gövde boş liste sayılır.
KaydetPOST {base}/{processKey}2xx + { "id": "…" }
SilDELETE {base}/{processKey}/{id}2xx (gövde okunmaz)
Varsayılan yapPUT {base}/{processKey}/{id}/default2xx
Varsayılanı kaldırDELETE {base}/{processKey}/default2xx

İstekler sihirbazın diğer çağrılarıyla aynı başlıkları taşır (Authorization: Bearer …, Accept-Language) ve provider'daki fetch'i kullanır; yalnızca POST gövdelidir (Content-Type: application/json). Kullanıcı kimliği gönderilmez — sunucu kullanıcıyı oturumdan/token'dan çözmeli ve kimse başkasının profiline erişememelidir. DELETE …/{id} ile DELETE …/default aynı biçimde olduğundan sunucuda {id} rotasını kısıtlayın (ör. {id:guid}); aksi halde default bir kimlik gibi silme rotasına düşer.

Liste satırı. Alan adları camelCase ya da PascalCase olabilir (id/Id, name/Name, mapping/Mapping, isDefault/IsDefault, uDate/UDate):

[
  {
    "id": "8d0c6a7e-1b2f-4c55-9a1e-3f6b2d7c9e10",
    "name": "Tedarikçi Excel'i",
    "mapping": "{\"version\":1,\"fields\":{…}}",
    "isDefault": true,
    "uDate": "2026-10-31T09:12:00"
  }
]

id ya da name'i boş olan, mapping'i metin olmayan, JSON olarak ayrıştırılamayan veya fields nesnesi taşımayan satırlar sessizce atlanır. uDate isteğe bağlıdır.

Kaydetme gövdesi:

{
  "name": "Tedarikçi Excel'i",
  "mapping": "{\"version\":1,\"fields\":{…}}",
  "isDefault": true
}

isDefault: true varsayılan yapar, false varsayılanlıktan çıkarır, alan hiç yoksa varsayılan durumu değişmez. Kaydetme isteği kimlik taşımaz; çubuk aynı adı Güncelle olarak sunduğu için sunucu kaydı kullanıcı + process key + ad üzerinden ekleyip güncellemelidir. Sihirbaz listedeki ilk isDefault profili uyguladığından süreç başına tek varsayılan tutulmalıdır.

mapping metni sunucu için opaktır — olduğu gibi saklanıp geri döndürülmelidir. İçeriği { version: 1, fields } biçimindedir; okurken yalnızca fields kullanılır:

{
  "version": 1,
  "fields": {
    "ExpCode": { "sources": ["Masraf Kodu"], "separator": " - " },
    "ExpName": { "sources": ["Kod", "Ad"], "separator": " - " },
    "Note": null
  }
}

fields tipi ColumnMappingProfileFields'tır: anahtar sözleşmedeki kolonun name'i, sources sırasıyla Excel başlık metinleri (tekrarlayan başlıklar Başlık (2) biçiminde), null ise bilerek eşlenmemiş alan.

2xx dışı her yanıt "{METOT} {url} → {durum}" mesajlı bir hata olarak çubukta görünür.

Özel depo — ColumnMappingProfileStorage

Profilleri başka bir yerde tutmak ya da farklı bir API'ye bağlamak için mappingProfileStorage'a bu arayüzü uygulayan bir nesne verin:

interface ColumnMappingProfileStorage {
  list(processKey: string): Promise<ColumnMappingProfile[]>;
  save(processKey: string, request: ColumnMappingProfileSaveRequest): Promise<{ id: string }>;
  remove(processKey: string, id: string): Promise<void>;
  setDefault(processKey: string, id: string | null): Promise<void>; // null → varsayılanı kaldır
}

interface ColumnMappingProfile {
  id: string;
  name: string;
  isDefault: boolean;
  fields: ColumnMappingProfileFields;
  updatedAt?: string | null;
}

interface ColumnMappingProfileSaveRequest {
  name: string;
  fields: ColumnMappingProfileFields;
  isDefault?: boolean; // true → varsayılan yap, false → çıkar, undefined → değiştirme
}

Kullanıcıyı depo belirler; arayüz kullanıcı kimliği almaz. Her save, remove ve setDefault çağrısından sonra liste list ile yeniden okunur.

import type { ColumnMappingProfile, ColumnMappingProfileStorage } from "@dataliva/livalib";

const anahtar = (processKey: string) => `import-profiles:${processKey}`;
const oku = (processKey: string): ColumnMappingProfile[] =>
  JSON.parse(localStorage.getItem(anahtar(processKey)) ?? "[]");
const yaz = (processKey: string, list: ColumnMappingProfile[]) =>
  localStorage.setItem(anahtar(processKey), JSON.stringify(list));

// Modül düzeyinde: referans sabit kalır.
export const yerelProfilDeposu: ColumnMappingProfileStorage = {
  list: async processKey => oku(processKey),
  save: async (processKey, request) => {
    const list = oku(processKey);
    const mevcut = list.find(p => p.name === request.name);
    const id = mevcut?.id ?? crypto.randomUUID();
    let next = [
      ...list.filter(p => p.id !== id),
      { id, name: request.name, fields: request.fields, isDefault: request.isDefault ?? mevcut?.isDefault ?? false },
    ];
    if (request.isDefault) next = next.map(p => ({ ...p, isDefault: p.id === id }));
    yaz(processKey, next);
    return { id };
  },
  remove: async (processKey, id) => yaz(processKey, oku(processKey).filter(p => p.id !== id)),
  setDefault: async (processKey, id) =>
    yaz(processKey, oku(processKey).map(p => ({ ...p, isDefault: p.id === id }))),
};

<LivaGridConfigProvider dataExchange={{ baseUrl, mappingProfileStorage: yerelProfilDeposu }}>

Depo nesnesini modül düzeyinde ya da useMemo ile sabit tutun. Her render'da yeni bir nesne verilirse sihirbaz açıkken profil listesi her render'da yeniden istenir.

Hazır REST deposunu kendi yapılandırmanızla kurmak için createColumnMappingProfileApiStorage(config, baseUrl) kullanılabilir; config bir LivaGridConfig ya da onu döndüren bir fonksiyondur (token, lang ve fetch buradan okunur). mappingProfilesUrl verildiğinde livalib bunu provider yapılandırmasıyla kendisi kurar.

Serbest kullanım — useDataExchange

Sihirbaz dışında kendi arayüzünüzü kurmak için düşük seviyeli kanca. Uç nokta çözümlemesi, başlıklar ve routeParams'ın her isteğin gövdesine route olarak eklenmesi sizin yerinize yapılır.

const dx = useDataExchange({ processKey: "expense-definition", routeParams: { budgetYear } });

const blob = await dx.downloadTemplate(true);   // verili şablon
saveBlobAs(blob, "masraf-tanimi.xlsx");

const onizleme = await dx.validate(file);
if (onizleme.errors.length === 0) {
  const rapor = await dx.importFile(file);
}
MetotDönerAçıklama
fetchContract()DataExchangeContractSözleşmeyi okur.
downloadTemplate(withData?)BlobwithData: true verili şablon indirir (POST).
exportData(payload?)BlobDışa aktarma; payload nesnesinin alanları gövdeye route ile birlikte yazılır.
validate(file)DataExchangePreviewDosyayı doğrular (multipart).
validateRows(rows)DataExchangePreviewDüzenlenmiş satırları doğrular.
validateSource(kind, options)DataExchangePreviewAdaptör kaynağını doğrular.
importFile(file)DataExchangeReportDosyayı içe aktarır.
importRows(rows)DataExchangeReportSatırları içe aktarır.
importSource(kind, options)DataExchangeReportAdaptör kaynağından içe aktarır.
fetchAdapters(capability)DataExchangeAdapterDescriptor[]'read' veya 'write' kataloğu.
endpointsDataExchangeEndpointsÇözümlenmiş uç noktalar.

useContract(opts) yalnızca sözleşmeyi getirir ve { contract, loading, error } döner — opts.contract verilmişse istek atılmaz. saveBlobAs(blob, filename) dönen blob'u tarayıcıya indirtir.

Sözleşme tipleri

DataExchangeContract sunucunun bildirdiği şemadır:

AlanTipAçıklama
processKeystringSüreç anahtarı
displayNameKeystring | nullÇeviri anahtarı
headerPolicy{ technicalSuffix, suffixFormat }Şablon başlık biçimi
upsertKeystring[]Kaydın kimliğini oluşturan alanlar
columnsColumnContract[]Kolon şeması
autoProvisionsAutoProvisionContract[]Eksik kayıt otomatik oluşturma kuralları

ColumnContract: name, header, dataType, required, order, groupHeader?, lookup? (registry / codeField / idField / labelField), monthSeries? (prefix / count / countFrom / labelKey), enum? (enumName / values / labelResource) ve autoProvision?.

DataExchangePreview: contract, rows, errors, totalRows, plannedInsertCount?, plannedUpdateCount?.

DataExchangeReport: totalRows, insertedCount, updatedCount, errorCount, autoProvisionedCount, recordsBefore?, recordsAfter?, errors, messages.

CellError: rowNumber, column?, bucket, message — bucket hatanın sınıfıdır (biçim, lookup, zorunluluk vb.) ve önizlemede gruplama için kullanılır. column kolonun name değerini taşımalıdır: önizlemede hatalı hücre ve kolon eşleştirme adımında hatalı alan bununla bulunur.

Adaptörler

Bir adaptör, verinin nereden okunacağını tanımlar. Katalog sunucudan gelir; AdapterPicker ve AdapterFieldsForm bileşenleri sihirbazın ilk adımını oluşturur ve dışa da açılmıştır.

AlanTipAçıklama
keystringAdaptör kimliği
displayNameKey / descriptionKeystringÇeviri anahtarları
iconstring | nullDevExtreme ikon adı
capabilitiesstring[]["read"] veya ["read", "write"]
transport'multipart' | 'json' | 'source'Kullanılacak HTTP yüzeyi
selectablebooleanKatalogda kullanıcıya gösterilir mi
fieldsAdapterField[]Kaynak adımında doldurulacak alanlar

AdapterField: name, labelKey, type ('file' \| 'text' \| 'lookup'), required, lookupKey? ve lookupContext?. lookupContext değerleri "${processKey}" biçiminde şablon değişkeni içerebilir; sihirbaz bunları kendi durumundan (processKey + routeParams) doldurur.

transport: "json" yalnızca sihirbazın kendi iç satır akışı içindir; kullanıcıya sunulan bir adaptör için multipart ya da source kullanılır.