sozsoft-platform/.github/instructions/lowcode-reference.instructions.md
2026-10-11 22:57:23 +03:00

1793 lines
170 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Low-Code Genişletilmiş Referans (JSON Kolon ve Modül Sözleşmeleri)
`lowcode.instructions.md` **nasıl üretileceğini** anlatır; bu dosya **neyin ayarlanabildiğini**
tek tek listeler. Orada "bunun bir JSON kolonu var" denen her şeyin alan alan karşılığı burada
durur: ListForm/ListFormField'ın tüm JSON kolonları, grafik sözleşmesi, Visual Designer envanteri
ve zamanlanmış e-posta modülü. Son bölüm (§12) Saas, Administration ve İK (Hr) menülerindeki **hazır
gelen bütün ekranları** ve bunların yeni bir komponentte nasıl kullanılacağını listeler.
**Kullanım sırası:** `ai.instructions.md` → `lowcode.instructions.md` → **bu dosya** → `README.md`.
Bir istek geldiğinde önce ana dosyadaki karar sırasını işlet; "hangi anahtarı yazacağım?"
sorusunda buraya bak.
**Kaynak:** Buradaki her tablo `api/src/Sozsoft.Platform.Application.Contracts/ListForms/**`
(DTO), `api/src/Sozsoft.Platform.Domain/Entities/Tenant/ListForm/ListForm{,Field}.cs` (entity),
`ui/src/views/{list,form}/**` (alanın gerçekten okunup okunmadığı) ve §12 için
`configs/seeds/data/App.Menus.*.json` + `ListFormSeeder_*.cs` dosyalarından çıkarılmıştır.
Çelişki hâlinde **kod esastır**; bu dosya güncellenir.
**Ortak kurallar**
- Bütün `*Json` kolonları veritabanında **tek satır JSON string** olarak durur. Wizard seed
dosyasının `ListForm`/`Fields` bölümleri ekranın DTO sözleşmesini taşıdığı için orada bu
kolonlar kaçışlanmış metin değil, tipli karşılıkları (`SchedulerOptionJson` →
`SchedulerOptionDto`) olarak yer alır; kaçışlama yalnızca `EditorOptions`, `EditorScript`,
`LookupQuery` gibi gerçekten metin olan alanlar içindir.
- Varsayılan değeri olan bir alanı tekrar yazmak zorunda değilsin; yalnızca farklı olanı yaz.
- `Accepted Values` yazan yerlerde değer birebir eşleşmelidir (büyük/küçük harf dahil).
- **Bilinmeyen anahtar sessizce düşer — tarayıcıya hiç ulaşmaz.** `GridOptionsDto` (ekran) ve
`ColumnFormatDto` (alan) `*Json` kolonlarının ham metnini `[JsonIgnore]` ile gizler ve istemciye
yalnızca tipli DTO'ya (`SchedulerOptionJson` → `SchedulerOptionDto`) çözülmüş hâlini yollar.
Dolayısıyla:
- DTO'da olmayan bir DevExtreme seçeneğini `*Json`'a yazmak **işe yaramaz**; DTO'ya alan
eklemek Core'dur ve yalnızca "core sisteme ekle" komutuyla yapılır (`lowcode` §0.8); eklenince
bu dosya da güncellenir.
- Çözme `JsonSerializer`'ın varsayılan ayarlarıyla yapılır: anahtarlar **PascalCase ve
büyük/küçük harfe duyarlıdır**. `"visible"` yazılan yerde `"Visible"` beklenir; küçük harfli
anahtar hata vermez, sessizce düşer. İstemcinin gördüğü camelCase hâl API çıktısıdır, seed'e
yazılmaz.
- Yanlış **tipte** yazılan değer (`"3"` yerine `3`, dizi yerine metin) `JsonSerializer`
tarafında `JsonException` fırlatır ve ekranın metadata isteği tamamen düşer — sessiz
değil, gürültülü bir hatadır. Tipleri tablodaki gibi yaz.
- Bu kural yalnızca alan seviyesindeki `ListFormField.EditorOptions` için geçmez: orası
ham metin olarak taşınır, tanımadığı anahtarı doğrudan editöre geçirir (§9.8).
---
## 0. Üretim yetenek envanteri — bir komponent üretirken tara
Bir ekran/komponent üretirken **her katmanı sırayla geç**. Cevabı "gerekmiyor" olan katmanı atla,
ama hiçbirini görmeden bırakma; kullanıcı "şunu da yapabilir miydik?" dediğinde cevap
"kod yazmamız gerekir" olmasın.
| # | Katman | Sorulacak soru | Nerede |
| --- | --- | --- | --- |
| 0 | **Hazır yetenek** | Bu talebin karşılığı Saas/Administration/İK menüsünde zaten var mı? | §12.6 |
| 1 | **Veri** | Tablo/view var mı, join gerekiyor mu, anahtar ve tipi ne? | `lowcode` §8 · §9.6 |
| 2 | **Ekran türü** | Liste mi, tek kayıt formu mu, serbest yerleşim mi? Kaç görünüm açılacak? | `lowcode` §0.6.1 · §9.8 · §5.4 |
| 3 | **Sütunlar** | Editör tipi, biçim, hizalama, genişlik, band, gizli/aktif | `lowcode` §4.3 · §9.7 |
| 4 | **Lookup** | Sabit liste mi, sorgu mu, servis mi? Cascade var mı? | `lowcode` §4.3.4 |
| 5 | **Varsayılanlar** | Kullanıcı/tarih/tenant/sequence/sorgu ile otomatik dolacak alanlar | `lowcode` §4.3.5 · §9.9 |
| 6 | **Alan davranışı** | Hesap, kopyalama, temizleme, koşullu kilit, uyarı, servis çağrısı | `lowcode` §5 |
| 7 | **Editör görünümü** | Maske, ondalık, tarih biçimi, arama, çoklu seçim, görsel yükleme, kamera/barkod/konum | `lowcode` §6 · §9.8 |
| 8 | **Doğrulama** | Zorunluluk, aralık, desen, karşılaştırma + sunucu tarafı kontrol | `lowcode` §4.3.6 · §4 |
| 9 | **Filtre/arama** | Filtre satırı, başlık filtresi, arama paneli, ek filtre çubuğu, grup paneli | §2.6 · `lowcode` §4.3.8 |
| 10 | **Özet** | Toplam/adet satırı, grup özetleri, pivot | §9.5 · §6 |
| 11 | **Biçimlendirme** | Koşullu satır/hücre rengi, CSS sınıfı | `lowcode` §4.3.7 |
| 12 | **Düzenleme deneyimi** | Popup/satır/hücre/batch, form grupları, kopyalama, toplu silme | §3 |
| 13 | **Aksiyonlar** | Hazır toolbar/satır butonları, URL parametreleri, not/ek/geçmiş, içe-dışa aktarma; özel butonlar: adres, diyalog, script, yetki, koşullu görünürlük | §5.8 · `lowcode` §4.9 |
| 14 | **Yazma mantığı** | Özel SQL, before/after kancaları, farklı servis adresi | §4 |
| 15 | **Ana-detay** | Alt form sekmeleri ve ilişki alanları | §5.2 |
| 16 | **Gösterge** | Üstte KPI kartları, grafik | §5.3 · §8 |
| 17 | **Süreç** | Onay akışı, bilgilendirme, durum alanları | `lowcode` §4.7 |
| 18 | **Otomasyon** | Zamanlanmış iş, zamanlanmış e-posta, bildirim kuralı | `lowcode` §9.10–§9.11 · §11 |
| 19 | **Çıktı** | Hazır grid raporu ya da `Raporlar` kategorisinde `DynamicReport` şablonu (kayıt alanları `{{AlanAdi}}`) | `lowcode` §9.12 |
| 20 | **Yetki** | Ekran, alan, komut ve buton yetkileri; menü görünürlüğü | §5.1 · `lowcode` §4.2 |
| 21 | **Tenant/şube/satır kısıtı** | `IsTenant`, `IsBranch`, `IsOrganizationUnit`, sorgularda kırılım, rol/kullanıcıya `ServerWhere` | `lowcode` §4.2 · §2.5 |
| 22 | **Dil** | Menü, başlık, alan, buton, mesaj metinleri EN + TR | `lowcode` §4.2 |
| 23 | **Kullanıcı tercihi** | Grid durumunun saklanması, kullanıcı/rol/dil varyantı | §2.5 · §5.7 |
| 24 | **Seed** | Üretilen her artefaktın dosyası + yalnızca DB'de yaşayan kayıtların geri getirici script'i | `lowcode` §0.2 · §9.14 |
Serbest yerleşimli bir komponent (Visual Designer) üretiyorsan 0–2 ve 13–24 aynen geçerlidir;
3–12 yerine tasarımcı envanteri (§10.1), `Form` sözleşmesi (§10.2), veri kaynağı/filtre
sözleşmesi (§10.4), bileşen prop sözleşmeleri (§10.5–§10.9), olay yükleri (§10.10) ve event
script'leri (`lowcode` §7.8) kullanılır. 20. katman (yetki) orada
iki yerdedir: düğüm başına `designerPermission` ve `Form`'un dört komut yetkisi.
Katmanların çoğunda cevap yeni bir artefakt değil, platformun **hazır bir ekranıdır**: numaratör,
bildirim, zamanlanmış iş, rapor, ayar, dil, yetki, şube, kısıt, dosya. Hepsinin envanteri §12'de —
"kod yazmamız gerekir" demeden önce §12.6'ya bak.
---
## 1. ListForm JSON kolon haritası
Bir List ekranının bütün davranışı bu kolonlardadır. "Şunu yapabilir miyiz?" sorusunun cevabı
önce bu tabloda aranır.
Aşağıdaki tablo `*Json` bloklarını listeler. Bunların **öncesinde** ekranın düz kolonları gelir
ve çoğu isteğin cevabı aslında oradadır:
| Kolon | Varsayılan | Ne yapar |
| --- | --- | --- |
| `ListFormCode` | — | Ekranın kimliği; route, metadata ve varyantlar bu koda bağlanır |
| `DataSourceCode` | `"Default"` | Hangi bağlantıdan okunacağı (`Sas_H_DataSource`) |
| `SelectCommandType` | `1` Table | `1` Table · `2` View · `3` TableValuedFunction · `4` Query · `5` StoredProcedure |
| `SelectCommand` | — | Tablo/view adı, fonksiyon çağrısı ya da sorgu metni |
| `TableName` | `SelectCommand` | Üretilen `SELECT`'te kolonların ve join'lerin önüne yazılan **alias**. Boşsa `SelectCommand` kullanılır. `4` Query'de **zorunludur**: sorgu `FROM (…) AS "{TableName}"` biçiminde türetilmiş tablo olarak sarılır ve boş bırakılırsa sorgu metni alias olur (SQL hatası). Wizard/MCP `deploy_wizard` Query ekranına `"Q"` (`WizardConsts.QuerySourceAlias`) yazar; fonksiyon çağrısında da kısa bir ad yaz |
| `KeyFieldName` · `KeyFieldDbSourceType` | — · `16` String | Anahtar kolon ve `System.Data.DbType` karşılığı. `4` Query'de ikisi de **zorunludur** (kolonlar katalogdan okunamaz); wizard/`deploy_wizard` anahtarsız Query ekranını reddeder, tip boşsa (`0`) seçilen alanlardaki karşılığından tamamlar. Ana tablo (en dıştaki `FROM`) `IsDeleted` taşıyorsa `DefaultFilter` yazılır ve sorgu `IsDeleted`'i seçmek zorundadır |
| `SelectFieldsDefaultValueJson` | — | ListForm editöründe (Veritabanı › Select) düzenlenir ama runtime **okumaz** (`DefaultValueManager` Select'te `FormFieldsDefaultValueJson`'ı kullanır). Okuma koşulu `DefaultFilter`'a token'la yazılır (`lowcode` §4.3.5) |
| `DefaultFilter` | — | Her sorgunun sonuna `AND` ile eklenen sabit `WHERE` koşulu. `DefaultValueHelper` üzerinden geçer, yani `@USERID`/`@TENANTID`/`@NOW` gibi token'lar çözülür. Soft delete'li tabloda (`IsDeletedField: true`) wizard buraya `"IsDeleted" = 'false'` yazar |
| `SortMode` | — | **Metin**: `"single"` \| `"multiple"` \| `"none"` (`PlatformConsts.GridOptions.SortMode*`; wizard `single` yazar) |
| `PageSize` | `50` (DB) | Sayfa başına kayıt; `0` = tümü. C# varsayılanı `0`, veritabanı varsayılanı `50`; boşsa grid `20` kullanır |
| `Name` · `Title` · `Description` | — | Ekran adı, başlığı, açıklaması — **dil anahtarı** yazılır |
| `IsTenant` · `IsBranch` · `IsOrganizationUnit` | `IsMultiTenant` · `false` · `false` | Otomatik kırılım filtresi; `IsTenant` ayrıca bağlantı çözümünü belirler (`lowcode` §9.3.3) |
| `SeedFilePath` · `SeedSyncInsert` · `SeedSyncUpdate` · `SeedSyncDelete` | — · `false` | Ekranın verisini `data/` altındaki dosyaya aynalar; `.json` uzantılı değer tek dosya, uzantısız değer klasör (`lowcode` §9.15) |
| Kolon | Ne yapılandırır | Bölüm |
| --- | --- | --- |
| `ColumnOptionJson` | Grid genel görünümü (kenarlık, satır rengi, kolon seçici, sabitleme) | §2.1 |
| `CommandColumnJson` | Toolbar ve satır butonları | `lowcode` §4.9 |
| `RowJson` | Satır yüksekliği ve metin kaydırma | §2.2 |
| `PagerOptionJson` | Sayfalama şeridi | §2.3 |
| `SelectionJson` | Seçim modu ve checkbox davranışı | §2.4 |
| `StateStoringJson` | Kullanıcının grid tercihlerinin saklanması | §2.5 |
| `FilterRowJson` · `HeaderFilterJson` · `FilterPanelJson` · `SearchPanelJson` · `GroupPanelJson` | Filtre/arama/gruplama panelleri | §2.6 |
| `ExtraFilterJson` | Ekran üstü ek filtre çubuğu | `lowcode` §4.3.8 |
| `EditingOptionJson` | Düzenleme modu, izinler, popup ayarları | §3.1 |
| `EditingFormJson` | Düzenleme formunun grup/sekme yerleşimi | §3.2 |
| `InsertCommand` · `UpdateCommand` · `DeleteCommand` (+ `*Before`/`*After`) | Özel SQL komutları ve kancalar | §4 |
| `*FieldsDefaultValueJson` | Otomatik doldurulan alanlar | `lowcode` §4.3.5 |
| `*ServiceAddress` | Yazma işlemini karşılayan uç | §4.3 |
| `PermissionJson` | Ekran yetkileri | §5.1 |
| `SubFormsJson` | Alt form sekmeleri | §5.2 |
| `WidgetsJson` | KPI kartları | §5.3 |
| `WorkflowJson` | Onay akışı | `lowcode` §4.7 |
| `LayoutJson` | Açık görünümler ve varsayılan | §5.4 |
| `PivotOptionJson` | Pivot görünümü | §6 |
| `TreeOptionJson` · `GanttOptionJson` · `SchedulerOptionJson` · `TodoOptionJson` | Ağaç/Gantt/Takvim/Kanban eşlemeleri | §7 |
| `SeriesJson`, `CommonJson` ve diğer 19 grafik kolonu | Chart görünümü (21 kolon; `DataSourceJson` okunmaz) | §8 |
| `ExportJson` | Grafik dışında **Grid/Pivot dışa aktarmayı da** açar: `Enabled` + `AllowExportSelectedData`; Grid yalnız `pdf`/`xlsx`, Pivot yalnız `xlsx`. Grid düğmesi ayrıca `PermissionJson.E` ister | §8.4 · §5.8 |
| `CustomJsSourcesJson` · `CustomStyleSourcesJson` | Sayfaya enjekte edilen JS/CSS | §5.5 |
| `Width` · `Height` · `FullHeight` | Ekran boyutu. `Height > 0` sabit piksel yüksekliği verir. Grid/Tree/Gantt/Scheduler'da `FullHeight` bayrağı görünümü `calc(100vh - …)` ile sayfaya yayar; **TodoBoard** bayrağa bakmaz, `Height` verilmediyse panoyu her zaman sayfanın kalan yüksekliğine yayar (kolonlar kendi içinde kayar), alt form olarak açıldığında ise içeriği kadar yer kaplar. | — |
| `ListFormType` · `IsSubForm` · `SubFormsListFormType` · `ShowNote` | Ekranın türü ve alt form davranışı | §5.6 |
| `CultureName` · `UserId` · `RoleId` | Ekranın dil varyantı (`UserId`/`RoleId` okunmaz — alan seviyesinde çalışır) | §5.7 |
---
## 2. Grid görünümü
### 2.1 `ColumnOptionJson` (`GridColumnOptionDto`)
| Anahtar | Varsayılan | Anlam |
| --- | --- | --- |
| `ShowBorders` | `true` | Dış kenarlık |
| `ShowRowLines` | `true` | Satır çizgileri |
| `ShowColumnLines` | `false` | Kolon çizgileri |
| `RowAlternationEnabled` | `true` | Tek/çift satır renklendirme |
| `AllowColumnReordering` | `true` | Kullanıcı kolon sırasını değiştirebilir |
| `AllowColumnResizing` | `true` | Kolon genişliği değiştirilebilir |
| `ColumnResizingMode` | `"widget"` | `nextColumn` \| `widget` |
| `ColumnAutoWidth` | `false` | İçeriğe göre genişlik |
| `ColumnFixingEnabled` | `false` | Kolon sabitleme menüsü |
| `ColumnChooserEnabled` | `false` | Kolon seçici düğmesi |
| `ColumnChooserMode` | `"dragAndDrop"` | `dragAndDrop` \| `select` |
| `ColumnHidingEnabled` | `true` | Dar ekranda kolon gizleme |
| `HoverStateEnabled` | `false` | Satır üzerine gelince vurgulama |
| `FocusedRowEnabled` | `true` | Odaklı satır |
| `ShowColumnHeaders` | `true` | Başlık satırı |
| `RtlEnabled` | `false` | Sağdan sola |
### 2.2 `RowJson` (`GridRowDto`)
| Anahtar | Varsayılan | Anlam |
| --- | --- | --- |
| `RowHeight` | `"auto"` | Satır yüksekliği (CSS) |
| `WhiteSpace` | `"normal"` | Uzun metnin kaydırılması |
| `OverflowWrap` | `"break-word"` | Kelime kırma |
### 2.3 `PagerOptionJson` (`GridPagerOptionDto`)
Adı yalnızca sayfalamayı çağrıştırsa da bu blok **kaydırma ve yükleniyor panelini** de taşır.
| Anahtar | Varsayılan | Anlam |
| --- | --- | --- |
| `Visible` | `true` | Sayfalama şeridi |
| `ShowPageSizeSelector` | `false` | Sayfa boyu seçici |
| `ShowInfo` | `false` | "Page 1 of 4 (37 items)" bilgisi — **Grid okumaz** (sabit kapalı); Tree (boşsa `true`) ve Card okur |
| `ShowNavigationButtons` | `true` | İleri/geri düğmeleri |
| `AllowedPageSizes` | `"10,20,50,100"` | **Virgüllü metin**, dizi değil |
| `InfoText` | `"Page {0} of {1} ({2} items)"` | `{0}` sayfa · `{1}` toplam sayfa · `{2}` kayıt — yalnız Tree ve Card |
| `DisplayMode` | `"adaptive"` | `adaptive` \| `compact` \| `full` |
| `ScrollingMode` | `"standard"` | `standard` \| `virtual` \| `infinite` — uzun listelerde `virtual` |
| `LoadPanelEnabled` | `"auto"` | **Metin** alanıdır: `"auto"` \| `"true"` \| `"false"`. İstemci metni boolean'a çevirir (`resolveLoadPanelEnabled`); Grid, Tree, Pivot ve Card aynı kuralı kullanır |
| `LoadPanelText` | `"Loading..."` | Yükleniyor metni |
Sayfa başına kayıt sayısı ekranın `PageSize` alanındadır; `0` = tümü. `ScrollingMode` ile
`PageSize` birlikte düşünülür: `virtual`/`infinite` seçilip `PageSize: 0` yazılırsa sayfalama
devre dışı kalır ve tüm küme tek istekte çekilir. `ScrollingMode` ve `LoadPanel*` Pivot'ta da okunur.
### 2.4 `SelectionJson` (`SelectionDto`)
| Anahtar | Varsayılan | Değerler |
| --- | --- | --- |
| `Mode` | `"none"` | `single` \| `multiple` \| `none` |
| `AllowSelectAll` | `false` | |
| `SelectAllMode` | `"allPages"` | `allPages` \| `page` |
| `ShowCheckBoxesMode` | `"always"` | `always` \| `none` \| `onClick` \| `onLongTap` |
Toolbar butonları seçili satırlarla çalışır (`lowcode` §4.9); onay akışı varsa wizard `single` yazar.
Tree yalnız `Mode`'u okur (`recursive` `TreeOptionJson.RecursiveSelection`'dan gelir); `AllowSelectAll`,
`SelectAllMode`, `ShowCheckBoxesMode` Grid ve Card'da çalışır, `AllowSelectAll` Pivot'un başlık filtresinde
"tümünü seç"i de açar. `SubFormsListFormType: "List"` ekranında alt form sekmeleri seçili satırla
beslendiği için `Mode` `single`/`multiple` olmalıdır (§5.6).
### 2.5 `StateStoringJson` (`StateStoringDto`)
| Anahtar | Varsayılan | Anlam |
| --- | --- | --- |
| `Enabled` | `true` | Toolbar'daki **Grid durumunu kaydet / sıfırla** menüsünü açar. Kayıt **elle** yapılır (Grid/Tree/Pivot'ta otomatik kayıt kapalı); kayıtlı durum `Enabled`'dan bağımsız her açılışta yüklenir. Pivot'un kaydet/sıfırla düğmeleri her zaman görünür |
| `Type` | `"custom"` | **Okunmaz** — durum her zaman sunucuya (`ListFormCustomization`) yazılır; editör de yalnız `custom` sunar |
| `StorageKey` | — | Anahtar; boşsa sunucu `listFormGridStorage-{ListFormCode}` üretir |
| `SavingTimeout` | `5000` | Yalnız **TodoBoard**'da otomatik kayıt bekleme süresi — **`[Range(1500, 5000)]`**, dışına çıkan değer doğrulamada reddedilir |
Durum sunucuda `ListFormCustomization` kaydı olarak (`ListFormCustomizationTypeEnum.GridState = 2`)
saklanır, başka tarayıcıdan girildiğinde de korunur. Kayıt **kullanıcı > rol > genel** önceliğiyle
çözülür: `UserId`/`RoleId` boş `GridState` herkese, `RoleId` dolu olan o role varsayılan düzen verir —
`UserUiFilter` (paylaşılan kayıtlı filtre) için de aynısı geçerlidir. Seçili görünüm (grid/card/…) ve
Card'ın kart başına kolon sayısı ise sunucuda değil **tarayıcının localStorage**'ındadır.
Aynı `GridState` kaydı görünüm başına ayrı `FilterName` taşır: `list-`, `tree-`, `pivot-`, `todo-`,
`gantt-` ve form sayfası için `form-{StorageKey}` (`useListFormStateStoring`). Form sayfasının state'i
kullanıcının **alan sırasıdır** (`{"fieldOrder": {"Alan": 1, …}}`, grup içi sıra): araç çubuğundaki
**Form Düzeni** paneli (`FormFieldOrderPanel`) alanları sürükleyerek sıralar, form her taşımada canlı
değişir, Kaydet state'e yazar, Sıfırla son kayda, Fabrika Ayarlarına Dön `EditOrderNo` sırasına döner
(state boşaltılır). `EditOrderNo` ve alanın grubu değişmez; panel yalnızca `Enabled` açıkken görünür.
Aynı tablonun diğer türleri: `UserUiFilter = 1` (kaydedilmiş filtre, §2.6), `ServerJoin = 3` ve
`ServerWhere = 4` — `UserId`/`RoleId` boşsa herkese, doluysa o rol/kullanıcıya sunucu tarafında
eklenen join ve `WHERE` parçası (`SelectQueryManager`; `ServerWhere` token'ları `DefaultValueHelper`
ile çözülür). **Satır bazlı veri kısıtının** kod yazmadan kurulduğu yer burasıdır; ListForm
editörünün **Kişiselleştirme** sekmesinden yazılır.
**İstemci filtresi sözleşmesi** (`list-form-select/select` `filter` parametresi; grid, rapor, widget ve MCP
`query_list` aynı yoldan geçer): yalnızca DevExtreme filtre dizisi kabul edilir — `["Alan","op",değer]`,
`["Alan",değer]`, `[koşul,"and"|"or",koşul]`, `["!",koşul]`, iç içe gruplar. Alan ekranın alanı ya da anahtar
alan olmalıdır (değilse koşul `1 = 1` olur), operatör `= <> < <= > >= contains notcontains startswith
endswith` dışındaysa ve JSON bozuksa istek reddedilir; değer her zaman parametredir. Filtre tek grup
olarak paranteze alınır, ardından kiracı/şube/OU koşulları `AND` ile eklenir. Toplam/grup alanları da
ekranın alanlarından, toplam türü `sum avg min max count`'tan seçilir. "Tüm kayıtları sil"
(`createDeleteQuery`) ekranın silme yetkisini ve `AllowAllDeleting`'i birlikte ister.
### 2.6 Filtre, arama ve gruplama panelleri
**`FilterRowJson`** (`GridFilterRowDto`): `Visible` (`false`), `ApplyFilter` (`auto` \| `onClick`),
`AllowUserUiFilterSave` (varsayılan `true` — kullanıcı kendi filtresini kaydedebilir;
kayıt `ListFormCustomizationTypeEnum.UserUiFilter = 1` olarak tutulur). `AllowUserUiFilterSave` aynı zamanda
toolbar'daki **Rapor › Liste** düğmesini ve kayıtlı filtre seçim kutusunu açar; `ApplyFilter: onClick`
filtre satırına **Filtre uygula** düğmesi ekler. Filtre uygulanınca başlık paneli renkli kenarlık alır
(kayıtlı durum varsa farklı renk).
**`HeaderFilterJson`** (`GridHeaderFilterDto`): `Visible` (`false`), `AllowSearch` (`false`),
`Height` (`325`), `Width` (`252`), `SearchTimeout` (`500`). Grid, Tree ve Gantt yalnız `Visible`'ı okur
(arama kolon bazında `ColumnHeaderJson.AllowSearch`'tedir); diğer dört anahtar yalnız Card ve Pivot'ta çalışır.
**`FilterPanelJson`** (`FilterPanelDto`): `Visible` (`false`), `FilterEnabled` (`false`),
`Texts.ClearFilter` (`"Clear"`) / `Texts.CreateFilter` (`"Create Filter"`) /
`Texts.FilterEnabledHint` (`"Enable the filter"`), `CustomizeText`. Grid ve Tree yalnız `Visible`'ı okur;
`Texts` yalnız Card'da çalışır; `FilterEnabled` ve `CustomizeText` **hiçbir görünümde okunmaz**.
**`SearchPanelJson`** (`SearchPanelDto`): `Visible` (`false`), `Width` (`160`).
**`GroupPanelJson`** (`GroupPanelDto`): `Visible` (`false`), `AutoExpandAll` (`false`).
`AutoExpandAll: true` **fazladan bir sorgu daha** çalıştırır.
---
## 3. Düzenleme
### 3.1 `EditingOptionJson` (`GridEditingDto`)
| Anahtar | Varsayılan | Anlam |
| --- | --- | --- |
| `Mode` | `"popup"` | `batch` \| `cell` \| `row` \| `form` \| `popup` |
| `RefreshMode` | `"full"` | `full` \| `reshape` \| `repaint` |
| `AllowAdding` / `AllowUpdating` / `AllowDeleting` | `false` | Aksiyon izinleri (yetki ayrıca `PermissionJson`: Ekle `C`, Düzenle `U`, Sil `D`). `AllowDeleting` + `D` toolbar'a **Seçilileri sil** düğmesi de ekler (2+ satır seçiliyken görünür). `Mode: batch` + `AllowUpdating` + `U` toolbar'a Kaydet/Geri al getirir |
| `AllowAllDeleting` | `false` | **Tümünü sil (filtreli)** düğmesi: filtredeki bütün kayıtları, önce sayıyı gösterip onayla siler; `AllowDeleting` + `D` de gerekir |
| `AllowDuplicate` | `false` | Satırda **Kopyala**: onay sorar, `list-form-data/duplicate` (`InsertCommand` + kancalar + `InsertFieldsDefaultValueJson`). Düğme **`C`** (Create) yetkisi ister |
| `AllowDetail` | `false` | Satırda Detay düğmesi — `/admin/form/{kod}/{id}` yeni sekmede; **`U`** yetkisi ister (`R` değil) |
| `OpenDetailAfterInsert` | `false` | Popup'tan eklenen kayıt kaydedilince aynı sekmede kaydın form sayfasına (`/admin/form/{ListFormCode}/{Id}`) geçilir. Grid, Tree, Card ve Scheduler'da çalışır; anahtar insert yanıtından alınır, dönmezse geçilmez. Wizard cevabında da aynı adla vardır (`ListFormWizardDto.OpenDetailAfterInsert`); ne zaman açılacağı `lowcode` §4.2 Davranış tablosunda |
| `ConfirmDelete` | `true` | Silme onayı |
| `UseIcons` | `false` | Aksiyonlar ikon olarak |
| `NewRowPosition` | `"viewportTop"` | `first` \| `last` \| `pageTop` \| `pageBottom` \| `viewportTop` \| `viewportBottom` |
| `StartEditAction` | `"click"` | `click` \| `dblClick` |
| `SelectTextOnEditStart` | `false` | Düzenlemeye girince metni seç |
| `EditColumnName` | — | **Ölü ayar** — editörde var ama komut sütununa başlık verilmez |
| `SendOnlyChangedFormValuesUpdate` | `false` | Update'te yalnızca değişen alanları gönder |
| `AddPageUrl` | — | Yeni kayıt popup yerine bu adreste açılır (`window.open`; PWA'da aynı sekme) |
| `AddDialogName` | — | Yeni kayıt kayıtlı bir diyalogda açılır (`CommandColumnDto.DialogName` ile aynı liste). `AddPageUrl` ile birlikte verilirse **diyalog önceliklidir**; diyalog kapanınca liste yenilenir |
| `AddDialogParameters` | — | `AddDialogName` diyaloguna geçilen sabit parametreler. Tipi **string**'dir: nesne değil, JSON metni yazılır (`"{\"id\":\"\"}"`); istemci `safeJsonParse` ile açar, bozuksa `{}` geçer. Toolbar'da satır bağlamı olmadığı için `@Kolon` token'i kullanılmaz |
| `Popup` | — | Aşağıdaki alt nesne |
`Popup` (`GridEditingPopupDto`): `Title`, `ShowTitle` (`true`), `Width` (`700`), `Height` (`500`),
`FullScreen` (`false`), `HideOnOutsideClick` (`true`), `ResizeEnabled` (`true`),
`DragEnabled` (`true`), `RestorePosition` (`true`),
`Position` (`center` \| `top` \| `bottom` \| `left` \| `right`).
- `Title` dil anahtarıdır (`::` eklenip çevrilir; önüne ✚/🖊️ konur).
- `Height` sabit yükseklik değil **azami yüksekliktir** (`maxHeight`; boşsa `90vh`), popup içeriğe göre büyür.
- Mobilde (`<768px`, `pointer:coarse` ya da `hover:none`) popup tam genişlik/tam ekran açılır, form tek
kolona iner; tam ekran aç/kapa düğmesi her zaman vardır.
- `DragEnabled`, `RestorePosition`, `Position` çalışır ama ListForm editöründe alanı yoktur — seed ile yazılır.
### 3.2 `EditingFormJson` (`EditingFormDto[]`)
Düzenleme formundaki grupların listesi. Alanların hangi gruba düştüğü `ListFormField` üzerindeki
`EditGroupOrderNo` / `EditOrderNo` ile belirlenir.
| Anahtar | Varsayılan | Anlam |
| --- | --- | --- |
| `Order` | `1` | Grubun sırası (`EditGroupOrderNo` ile eşleşir) |
| `ItemType` | `"group"` | `group` (kullanılan) \| `tabbed` \| `simple` \| `empty` \| `button` — bütün `tabbed` gruplar ilk `tabbed` grubun yerinde tek sekme şeridi olur (liste popup'ı ve form sayfası aynı) |
| `Caption` | — | Grup başlığı (dil anahtarı) |
| `ColCount` | `2` | Grup içi kolon sayısı |
| `ColSpan` | `2` | Grubun formda kapladığı kolon |
Wizard bu listeyi `Groups` bloğundan üretir; anahtar alan ve `IncludeInEditingForm: false`
olanlar dışarıda kalır.
---
## 4. Yazma komutları ve kancalar
### 4.1 Komutlar
| Kolon | Ne zaman |
| --- | --- |
| `InsertCommand` / `UpdateCommand` / `DeleteCommand` | Boşsa platform komutu kendi üretir; doluysa **bu SQL** çalışır |
| `InsertBeforeCommand` / `InsertAfterCommand` | Insert'ten önce/sonra |
| `UpdateBeforeCommand` / `UpdateAfterCommand` | Update'ten önce/sonra |
| `DeleteBeforeCommand` / `DeleteAfterCommand` | Delete'ten önce/sonra |
**Kancalar, kod yazmadan iş mantığı çalıştırmanın yeridir**: stok düşme, hareket kaydı, durum
güncelleme, log, bildirim kuyruğuna satır ekleme. Karmaşıklaşınca gövdeyi bir stored procedure'e
taşı ve kancadan onu çağır (`EXEC Wms_S_AfterOrderInsert @Id, @LastModifierId`).
- Kancalar ana komutla **aynı parametre sözlüğünü** kullanır.
- Insert'in ana komutu `ExecuteScalar` ile çalışır (SQL Server `OUTPUT Inserted.{Key}`, PG `RETURNING`);
özel `InsertCommand` anahtarı döndürmezse istemci yeni kaydın anahtarını alamaz (detaya gitme, alt form).
- Otomatik Update'te istekte `TenantId` varsa `WHERE`'e `AND TenantId` eklenir; otomatik Delete'te
**bütün parametreler** `WHERE` koşuludur.
- **Kopyala** (`list-form-data/duplicate`) `InsertCommand` + Insert kancaları + `InsertFieldsDefaultValueJson`
ile çalışır.
- Yazmadan sonra ekranın seed senkronu çalışır (`SeedSync*`, `lowcode` §9.15).
### 4.2 Komut içinde kullanılabilen anahtarlar
Yazma komutlarında ve kancalarında **metin değiştirme yoktur** (`DefaultValueHelper` çalışmaz); yalnızca
Dapper parametreleri bağlanır:
| Parametre | Kaynağı |
| --- | --- |
| `@{FieldName}` | İstekte gelen alan — Insert/Update'te yalnızca alan seviyesinde `C`/`U` yetkisi olanlar (Delete'te yetki süzgeci yok) |
| `@{FieldName}` | `*FieldsDefaultValueJson` öğesi — adı token değil **alan adıdır**: `@CreatorId`, `@CreationTime` (`@USERID` değil) |
| `@TenantId` | `IsTenant` açıksa |
| `@{KeyFieldName}` | Yalnızca **otomatik üretilen** Update/Delete'te eklenir. Özel `UpdateCommand`/`DeleteCommand`'da anahtar parametresi gelmez; wizard bunu `DeleteFieldsDefaultValueJson`'a `{FieldName:"Id", Value:"@ID", CustomValueType:2}` yazarak çözer (`DefaultDeleteCommand` → `… WHERE "Id" IN @Id`) |
Bu yüzden `@USERID`, `@USERNAME`, `@ROLES`, `@ID` komut metninde **çalışmaz** ("Must declare scalar
variable"); kullanıcıya/zamana bağlı değer gerekiyorsa önce ilgili `*FieldsDefaultValueJson`'a alan adıyla
yazılır (`{FieldName:"LastModifierId", Value:"@USERID", CustomValueType:2}`), komutta `@LastModifierId`
kullanılır (`lowcode` §4.3.5). **String birleştirme ile sorgu kurma.**
**Metin değiştirmenin çalıştığı yerler** (`DefaultValueHelper.GetDefaultValue`; `@USERID`, `@USERNAME`,
`@ROLES`, `@DATE`, `@NOW`, `@DAY`, `@MONTH`, `@YEAR`, `@TENANTID`): `SelectCommand`, `DefaultFilter`,
kullanıcı `ServerWhere` özelleştirmesi, widget `SqlQuery`, `LookupQuery`, `DbQuery` varsayılanının
`SqlQuery`'si, Custom Endpoint parametre varsayılanları, rapor lookup'ları. Host bağlamında
`= '@TENANTID'` → `IS NULL`, yalın `@TENANTID` → `NULL` olur; `@ROLES` `','` ile birleşir
(`IN ('@ROLES')` kalıbı). `@ID`, `@NEWID`, `@SELECTED_IDS`, `@AUTONUMBER` metinde çözülmez;
`@BRANCHID`/`@LANG` diye token yoktur. Çalışmadığı yerler: yazma komutları ve kancaları, `ServerJoin`
özelleştirmesi, `ExtraFilterJson` (istemci filtresi olarak parametreleşir), grafik özet sorgusu.
**Örtük filtreler** (token değil, sorguya kendiliğinden eklenir): `IsTenant` → `TenantId = '{id}'`/`IS NULL`;
`IsBranch` → `BranchId IN (kullanıcının şubeleri)` — kullanıcının şubesi yoksa hiç satır gelmez;
`IsOrganizationUnit` → `OrganizationUnitId IN (birim + alt birimler)`; `ApprovalIsFilterUserName` →
onaycı filtresi. Widget SQL'i bu filtrelerden **geçmez** (kiracı koşulunu kendisi yazar). Yazma tarafında
şube/birim ataması yoktur — `BranchId` varsayılanla ya da formdan gelir.
### 4.3 `*ServiceAddress`
`InsertServiceAddress` / `UpdateServiceAddress` / `DeleteServiceAddress` varsayılan olarak
`list-form-data/insert|update|delete` uçlarıdır. Yazma işini bir Custom Endpoint ya da Dynamic
Service'e devretmek istiyorsan bu alanı o ucun adresiyle değiştirirsin — ekranın geri kalanı
(form, doğrulama, yetki) aynı kalır.
- Çağrı **her zaman POST**'tur ve adres `'/api/app/' + adres` olarak kurulur (baştaki `/` silinir);
`http…` ile başlayan mutlak adres de önek alır ve çalışmaz — dış servis bir Custom Endpoint / Dynamic
Service arkasına konur.
- Gövdeler: insert `{data, listFormCode}` · update `{keys:[key], data, listFormCode}` (yalnızca değişen
alanlar) · delete `{keys:[key], listFormCode}`.
- **Adres boşsa işlem sessizce yapılmaz**; TodoBoard ekle/güncelle/sil düğmeleri kapanır,
`UpdateServiceAddress` boşsa iş akışı toolbar'ı tamamen gizlenir. Salt okunur ekran için adresleri
boşaltmak yerine `EditingOptionJson`'daki izin bayraklarını kapat.
- Yetki ucun kendisinde (Create/Update/Delete) denetlenir; bağlantı `DataSourceManager.GetDataSourceAsync(IsTenant,
DataSourceCode)` ile çözülür (`DataSourceType` `1` Mssql · `2` Postgresql).
**Çok değerli alanlar bu iki yolda farklı davranır.** Varsayılan uçta payload bir sözlüktür
(`data: {...}`) ve `dxTagBox` dizisini `QueryHelper.GetFormattedValue` `|` ile birleştirir.
Tipli bir DTO alan uçta (`list-form-dynamic-api/...` gibi) böyle bir adım yoktur: dizi `string`
property'ye bağlanamaz ve istek `400` ile düşer. Çözüm, DTO alanına
`[JsonConverter(typeof(MultiValueStringJsonConverter))]` eklemektir — `Modules` alanında
uygulanmış örneği `CreateUpdateTenantInput` / `CreateTenantFromOrderInput` taşır. Yalnızca
**girdi** DTO'larına eklenir; okuma DTO'larında (`SettingDefinitionDto.Providers`,
`PlatformPermissionGrantInfoDto.Modules`) karşılığı yoktur çünkü o ekranlar varsayılan SQL
yolundan kaydeder.
---
## 5. Ekran seviyesindeki diğer bloklar
### 5.1 `PermissionJson` (`PermissionCrudDto`)
Tek harfli anahtarlar: `R` (read), `C` (create), `U` (update), `D` (delete), `E` (export),
`I` (import), `N` (note). Değerler **yetki adıdır**, boolean değil:
```json
{ "R": "App.Mrp.Orders", "C": "App.Mrp.Orders.Create", "U": "App.Mrp.Orders.Update",
"D": "App.Mrp.Orders.Delete", "E": "App.Mrp.Orders.Export", "I": "App.Mrp.Orders.Import",
"N": "App.Mrp.Orders.Note" }
```
Alan seviyesindeki karşılığı `ListFormField.PermissionJson`'dır ve **şeması farklıdır**
(`ListFormFieldPermissionDto`) — ekran seviyesindeki yedi harfli sözleşmeyi buraya kopyalama:
| Anahtar | Tip | Anlam |
| --- | --- | --- |
| `Deny` | bool | `true` ise alan **hiç dönmez**; diğer anahtarlara bakılmaz |
| `C` · `R` · `U` | string | Yaratma / okuma / güncelleme yetkisinin **adı**. Boşsa ekranın kendi Create/Read/Update yetkisi kullanılır |
| `E` | bool | Alan dışa aktarımda yer alsın mı — **yazılmazsa `false`**: `PermissionJson`'ı olmayan alan dışa aktarılmaz |
| `I` | bool | Yalnızca istemcide: içe aktarma şablonunun kolonlarını seçer; sunucu yok sayar |
```json
{ "C": "App.Mrp.Orders.Create", "R": "App.Mrp.Orders", "U": "App.Mrp.Orders.Update",
"E": true, "I": true, "Deny": false }
```
`ListFormField` üzerindeki `CanRead` / `CanCreate` / `CanUpdate` / `CanExport` kolonları
**veritabanında yoktur** (`[NotMapped]`); `ListFormFieldManager.SetPermissionsAsync` bu
JSON'u oturumdaki yetkilerle karşılaştırarak istek başına hesaplar. Üçü de `false` çıkan alan
metadata'dan tamamen düşer — yani okuma yetkisi olmayan bir kolon sorguya bile girmez.
`C`/`U` yetkisi olmayan alan sunucuda insert/update verisinden atılır ve formda **salt okunur**
görünür. Wizard varsayılan olarak `DefaultFieldPermissionJson(permissionName)` üretir.
### 5.2 `SubFormsJson` (`SubFormDto[]`)
| Anahtar | Anlam |
| --- | --- |
| `TabType` | Sekmenin görünümü — `ListFormTabTypeEnum`: `List` \| `Card` \| `Tree` \| `Gantt` \| `Scheduler` \| `Todo` \| `Form` \| `Chart` \| `Pivot`. `Form` ana kayda bağlı **tek** kaydı gösterir: kayıt yokken "Yeni" düğmesi (hedef ekranda `AllowAdding` açıksa), kayıt varken yalnızca düzenleme; ikinci kayıt açılmaz |
| `TabTitle` | Dil anahtarı |
| `Code` | Hedef `ListFormCode` |
| `IsRefresh` | `true` ise alt sekmede satır eklenip/değiştirilip/silindiğinde **ana form kaydı** yeniden çekilir (ana kayıt değişince alt sekme zaten her zaman yenilenir) — alt satırlardan hesaplanan başlık toplamı için açılır. MCP `deploy_wizard` yeni alt form bağlantısını `false` yazar; var olan bağlantıda önceki değer korunur |
| `Relation[]` | `ParentFieldName` → `ChildFieldName` + `DbType` (`System.Data.DbType`, **sayı**). İstemci yalnızca değeri dolu **ilk** satırı kullanır — bileşik anahtar desteklenmez |
Alt formun kendisi de bir ListForm'dur: `IsSubForm = true` olmalı ve genellikle `CreateMenu:
false` ile menüsüz üretilir (`lowcode` §4.2). `TabType` hedefin `LayoutJson` bayrağına bakmaz (Grid/Card/
Pivot bayrak kapalı olsa da çizilir); Tree/Gantt/Scheduler/Todo hedefte ilgili `*Expr` alanlarını ister.
- **`Form` sekmesi** kaydı ilişkiyle (`ChildFieldName = ana alanın değeri`) yükler; düzenlemede anahtar
yüklenen kayıttan okunur. Yeni kayıtta ilişki alanı ana kaydın değeriyle dolu ve kilitli açılır.
- **`Chart` sekmesi** ana ekranın grafik tanımını kullanır ve ilişkiyle süzülmez.
- Form sayfasında alt formlar görüntüle/düzenle modunda çizilir, yeni kayıtta çizilmez. Adres
`#{AltFormKodu}` ile açılırsa o sekme seçilir; sekme değişince hash yazılır.
### 5.3 `WidgetsJson` (`WidgetEditDto[]`)
Ekranın üstündeki KPI şeridi. **Sözleşmesi sezgisel değildir, dikkatle oku:**
| Anahtar | Ne yazılır |
| --- | --- |
| `SqlQuery` | Kartları besleyen sorgu. **Zorunludur** — boş olan widget hiç çizilmez |
| `Title` · `SubTitle` · `Value` · `Color` · `Icon` · `OnClick` | **Sorgu sonucundaki kolon adları** — sabit metin değil. `OnClick` kolonunun **değeri JS olarak `eval` edilir** (adres değil): `'window.open(''/admin/list/X'')' AS Tikla`. Sunucu her satırda bu adlı kolonu okur; kolon yoksa boş string döner. `Title`/`SubTitle` kolonunun **değeri** ekranda dil anahtarı olarak çözülür (`WidgetGroup`, `::` öneki eklenerek); karşılığı olmayan metin aynen basılır, bu yüzden sorguya dil anahtarı yazılır |
| `ColSpan` · `ColGap` · `ClassName` | Şeritteki yerleşim (widget başına, karta değil): `ColSpan` 12 kolonluk ızgarada kart genişliği (mobilde 6'ya zorlanır), `ColGap` Tailwind `gap-{ColGap}` eki (`3` önerilir; `16` = 4rem) |
| `ValueClassName` | Değerin CSS sınıfı, varsayılan `text-3xl` |
| `IsActive` | **Varsayılanı `false`'tur** — yazılmazsa sorgu hiç çalışmaz ve şerit boş kalır; her widget'a `true` yaz |
**Bir widget, sorgunun döndürdüğü her satır için bir kart çizer.** Yani tek bir widget kaydıyla
"Bekleyen / Onaylı / Reddedilen" üçlüsü tek sorgudan üretilebilir:
```sql
SELECT 'Bekleyen' AS Baslik, COUNT(*) AS Deger, 'amber' AS Renk, 'FaClock' AS Ikon
FROM Mrp_T_Order WHERE Status = 1
UNION ALL
SELECT 'Onaylı', COUNT(*), 'green', 'FaCheck' FROM Mrp_T_Order WHERE Status = 2
```
```json
{ "Title": "Baslik", "Value": "Deger", "Color": "Renk", "Icon": "Ikon", "ColSpan": 4, "IsActive": true }
```
Sorgu ekranın bağlantısında (`IsTenant` + `DataSourceCode` ile çözülen, `lowcode` §9.3.3),
**olduğu gibi** çalıştırılır:
- Ekranın otomatik tenant/şube/OU filtresi **uygulanmaz**; kırılım sorguya yazılır.
- Sorgu `DefaultFilter` ile aynı token çözümünden geçer (`DefaultValueHelper.GetDefaultValue`):
`@USERID`, `@USERNAME`, `@DATE`, `@NOW` ve `@TENANTID` metin olarak yerine konur.
Kiracı tablosunda (`IsTenant: true`) her karta `"TenantId" = '@TENANTID'` yaz; host'ta bu
`"TenantId" IS NULL`'a döner, yani kartlar liste ekranıyla aynı kümeyi sayar. Yazılmazsa ortak
veritabanında kartlar bütün kiracıların toplamını gösterir.
- `IsDeleted` koşulu widget sorgusuna yazılmaz (platform varsayılanı).
- Wizard / MCP ile üretilen List ekranı sorulmadan en az 3 kartla gelir; kart seçimi, istisnalar
ve metin/renk kuralı: `lowcode` §4.6.1.
- Seeder'da şerit `DefaultWidgetsJson(tablo, listFormName, tenant, params WidgetCard[])` ile
kurulur (`ListFormSeeder_DefaultJsons`): kart başına `Title` (dil anahtarı), renk, ikon, ek koşul
ve isteğe bağlı `Value` ifadesi; alt başlık ekranın adıdır, değer tip çakışmasın diye metne
çevrilir. Gruplamalı sorgu için `DefaultWidgetsJson(sql, kartSayısı)`. Genel kart başlıkları
`App.Widget.*` dil anahtarlarındadır.
- Kartlar GridDto ile gelir; Grid, Tree, Card, Gantt ve Scheduler görünümünde Yenile düğmesi ve
satır ekleme/güncelleme/silme kartları yeniden çeker (`useListFormWidgets`). Yalnızca widget
listesi değişir, GridDto değiştirilmez — görünüm durumu (sayfa, seçim) korunur. Kart sorgusu
ekranın filtresine bağlı olmadığı için filtre değişikliği kartları yenilemez. Pivot'ta şerit çizilir
ama yenilenmez.
- Kart sorgusu alt form sekmesinde `Relation` ile süzülmez; bu yüzden alt form olarak üretilen
ekrana widget yazılmaz (menüsüz ya da menülü). Menüsüz (`CreateMenu: false`) wizard'ın `Widgets`'ı
deploy'da ayrıca düşürülür; ekran alt form olarak (`SubForms` sekmesi, `isSubForm`) açıldığında
şerit çizilmez ve yenilenmez.
### 5.4 `LayoutJson` (`LayoutDto`)
| Anahtar | Varsayılan |
| --- | --- |
| `Grid` · `Card` · `Pivot` · `Chart` | `true` |
| `Tree` · `Gantt` · `Scheduler` · `Todo` | `false` |
| `DefaultLayout` | `"grid"` |
Wizard'daki görünüm bayraklarının karşılığıdır. Varsayılan **dörtlüdür**: `Grid`, `Card`, `Pivot`,
`Chart` her liste ekranında açık gelir; üretilen ekranda bunlardan biri yalnızca istek onu açıkça
dışarıda bıraktığında kapatılır (`lowcode` §9.8). Aynı varsayılan üç yerde tutulur ve birlikte
değişir: UI wizard başlangıç değerleri (`Wizard.tsx`), `ListFormWizardDto` ve `LayoutDto`
(+ `WizardConsts.DefaultLayoutJson`). Tree/Gantt/Scheduler/Todo zorunlu `*Expr` alanı ister
(§7); bayrak açık olsa bile alanlar boşsa istemci görünümü göstermez (`List.tsx` `isLayoutValid`)
ve seeder o görünümün JSON'unu yazmaz. `DefaultLayout`, `true` olan bir görünümü göstermelidir.
- **`Grid` bayrağı ölüdür**: Grid düğmesi her zaman çizilir, kapatılamaz.
- `Card` bayrağı yalnız düğmeyi gizler; `DefaultLayout: "card"` bayrak kapalıyken de açılır.
- Düğme koşulu `isLayoutValid`'den gevşektir: Tree düğmesi yalnız `ParentIdExpr`, Gantt düğmesi
`ParentIdExpr` + `TitleExpr` ister. Geçersiz `DefaultLayout` ya da kayıtlı görünüm `grid`'e düşer.
- Kullanıcının seçtiği görünüm sunucuda değil tarayıcının localStorage'ında (`admin.lists.states`) durur.
### 5.5 `CustomJsSourcesJson` / `CustomStyleSourcesJson`
Her ikisi de **string dizisidir**. Bir eleman `http` ya da `/` ile başlıyorsa dosya olarak
sayfaya eklenir (`<script src>` / `<link rel=stylesheet>`); aksi hâlde içerik olarak çalıştırılır
(JS'te `eval`, CSS'te inline `<style>`).
```json
["/assets/custom/orders.js", "console.log('orders ekranı yüklendi')"]
```
Kaynaklar Grid, Tree, Pivot, Gantt, Scheduler ve form sayfasında yüklenir; Card, TodoBoard ve
Chart yüklemez. ListForm editöründe alanı yoktur, yalnız seed ile yazılır.
**Son çaredir.** Önce `EditorScript`, `EditorOptions`, `ColumnStylingJson` ve `CommandColumnJson`
denenir. Buraya yazılan kod ekran her açıldığında çalışır ve hiçbir yerde denetlenmez.
### 5.6 Ekranın türü
| Kolon | Varsayılan | Anlam |
| --- | --- | --- |
| `ListFormType` | `"List"` | `ListFormTypeEnum`: `List` (ızgara) \| `Form` (tek kayıt) \| `Chart` (yalnız grafik). Route karşılıkları: `/admin/list/{kod}` · `/admin/form/{kod}[/{id}[/edit]]` · `/admin/chart/{kod}` |
| `IsSubForm` | `false` | Bilgi amaçlı — runtime okumaz; alt form davranışını sekmenin `isSubForm` prop'u belirler |
| `SubFormsListFormType` | `"Form"` | **Bu ekranın kendi** `SubFormsJson` sekmelerinin nerede çizileceği: `List` → liste sayfasında grid/tree/card'ın altında, **seçili satıra bağlı** ana-detay (`SelectionJson.Mode` `single`/`multiple` olmalı) · `Form` → yalnız form/detay sayfasında |
| `ShowNote` | `true` | Not paneli (alt form ya da iş akışı varsa wizard açar) |
`ListFormType = "Form"` listesiz, tek kayıtlık kart ekrandır; canlı örneği Administration ›
Licence'tır (§12.7); `/admin/form/{kod}` (id'siz) adresinde yeni kayıt yerine görüntüleme açılır ve
kaydı `DefaultFilter` seçer. ListForm editörü yalnız `List`/`Form` sunar (`Chart` seed ile yazılır).
`Pivot` bir `ListFormType` **değildir** — pivot, `LayoutJson.Pivot`
bayrağıyla açılan bir görünümdür ve `/admin/pivot/...` diye bir route yoktur.
### 5.7 Varyantlar
- **`ListForm`**: yalnızca `CultureName` varyantı çalışır (`ListFormManager.GetUserListForm` kültürle
süzer). `ListForm.UserId`/`RoleId` **hiçbir yerde okunmaz**; aynı kod ve kültürle ikinci satır açılırsa
herkese rastgele biri döner — ekran düzeyinde kullanıcı/rol varyantı açma.
- **`ListFormField`**: kullanıcı > rol > varsayılan; aktif UI kültürü > `DefaultLanguage` (`en`)
(`ListFormFieldManager`). Kullanıcı/rol bazlı farklı sütun buradan verilir.
- **Kullanıcı tercihi**: grid durumu, kayıtlı filtre ve `ServerWhere`/`ServerJoin` `ListFormCustomization`'da
durur (lowcode §3 `ListFormCustomizationType`).
- Kullanıcıya özel grafik serisi `ChartSeriesDto.UserId` ile verilir (§8.1).
Wizard varyant üretmez; ihtiyaç varsa ListForm editöründen açılır ve seed karşılığı elle yazılır.
### 5.8 Runtime'da hazır gelenler — toolbar, satır, URL, not, içe aktarma
Aşağıdakilerin hepsi ekranla **kendiliğinden** gelir; açmak için yalnızca gösterilen bayrak/yetki yazılır.
"Bunun için kod gerekir" demeden önce bu tabloya bak.
**Toolbar** (Grid/Tree/Card ve form sayfası; sıra sabittir — arama, ekle, yenile, rapor, akış, aç/kapa…;
`toolbarOrder.ts`):
| Öğe | Açan | Not |
| --- | --- | --- |
| Arama | `SearchPanelJson.Visible` | |
| Ekle | `AllowAdding` + `C` | `AddDialogName` > `AddPageUrl` > popup |
| Yenile | her zaman | Widget'ları da yeniler |
| Akış: Başlat / onay düğümleri / Sıfırla | `WorkflowJson` (`lowcode` §4.7) + `UpdateServiceAddress` dolu; Sıfırla için `ApprovalIsResetWorkflow` | Çoklu seçimde her satıra uygulanır |
| Tümünü genişlet/daralt | Tree görünümü | |
| Kolon seçici · Grup paneli | `ColumnOptionJson.ColumnChooserEnabled` · `GroupPanelJson.Visible` | |
| Seçilileri sil | `AllowDeleting` + `D` | 2+ satır seçiliyken |
| Tümünü sil (filtreli) | + `AllowAllDeleting` | Sayıyı gösterip onaylatır |
| Batch Kaydet / Geri al | `Mode: batch` + `AllowUpdating` + `U` | |
| Filtre uygula | `FilterRowJson.ApplyFilter: onClick` | |
| Rapor › Liste | `FilterRowJson.AllowUserUiFilterSave` | `/admin/reports/DynamicGrid` (Tree'de `DynamicTree`) — filtre, sıralama, kolon genişlikleri ve alt form süzgeci taşınır (`lowcode` §9.12). Form sayfasında **Rapor › Form** |
| Filtreyi kaydet/sil/temizle + kayıtlı filtre kutusu | `AllowUserUiFilterSave` | `UserUiFilter`; rol/genel paylaşılabilir (§2.5) |
| Sütunları sığdır | (`AllowUserUiFilterSave` ya da `StateStoringJson.Enabled`) + `ColumnOptionJson.ColumnAutoWidth` | |
| Grid durumunu kaydet/sıfırla | `StateStoringJson.Enabled` | Elle kayıt (§2.5) |
| İçe aktar | `PermissionJson.I` | Aşağıda |
| Dışa aktar | `ExportJson.Enabled` + `PermissionJson.E` (+ `AllowExportSelectedData`) | Grid: `pdf`, `xlsx`; Pivot: `xlsx`; Card/Tree/Gantt/Scheduler'da yok |
| Ek filtre çubuğu | `ExtraFilterJson` | Grid, Tree, Card (`lowcode` §4.3.8) |
| Yetkili kullanıcılar · Yönet | `App.Listforms.Listform.Update` | Ekranın R/C/U/D/E/I/N yetkisine sahip kullanıcı/rolleri listeler · ListForm editörünü açar (bütün görünümler + form sayfası) |
| Özel butonlar | `CommandColumnJson` (`lowcode` §4.9) | Grid, Tree, Card, Todo (yalnız Url/OnClick) ve form sayfası; Gantt/Scheduler/Pivot/Chart'ta yok |
**Satır butonları:** Düzenle (`AllowUpdating` + `U`), Sil (`AllowDeleting` + `D`), Detay (`AllowDetail` +
**`U`**), Not (`ShowNote` + `PermissionJson.N`), Kopyala (`AllowDuplicate` + `C`), özel butonlar. Card'da
butonlar kart başlığındadır; form sayfasında Düzenle/Sil/Detay dışındaki satır butonları ve toolbar
butonları **form toolbar'ına taşınır**.
**URL parametreleri** — menü/buton/widget adresinden ekranı süzülmüş ya da önceden doldurulmuş açmak için:
| Adres | Etki |
| --- | --- |
| `/admin/list/{kod}?filter=<DevExtreme filtre JSON>` | Sorguya `AND` eklenir; yeni satırda o alanlar bu değerle **dolu ve salt okunur** açılır; rapora taşınır |
| `/admin/list/{kod}?{Alan}=değer` | Grid'deki her kolon için `=` süzgeci |
| `/admin/list/{kod}?sort=[{"selector":"X","desc":true}]` | Açılış sıralaması |
| `/admin/form/{kod}?{Alan}=değer` | Yeni kayıtta alan doldurulur ve kilitlenir |
| `/admin/form/{kod}/{id}?filter=…` | Id süzgecine ek süzgeç |
| `/admin/form/{kod}/{id}#{AltFormKodu}` | O alt form sekmesi seçili açılır |
| `/admin/chart/{kod}?filter=…` | Grafik verisini süzer |
| Yazma istekleri | Sayfanın URL parametreleri yazma isteğine query olarak eklenir → `CustomValueType: 4` QueryParams (`lowcode` §4.3.5) |
**Not paneli** (satırdaki Not butonu ya da form sayfasının yan paneli; alt formda yok): `note`/`message`
türünde not, iş akışı kararları (`workflow` türü), dosya eki (dosya başına **2 MB**, indir/sil) ve
**Değişiklik geçmişi** sekmesi (kaydın audit log'u, son 200 Insert/Update/Delete). Ayrı ek/geçmiş ekranı
kurulmaz.
**İçe aktarma** (`PermissionJson.I`): Excel/CSV şablonu indirilir (kolonlar alan yetkisi `C` olanlar, `Id`
hariç); `.xlsx/.xls/.csv`, en çok **10 MB**; önizlemede satır seçimi, oturum ve çalıştırma geçmişi. Yalnız
**INSERT** yapar ve `QueryManager`'dan geçer: `Insert*Command` kancaları çalışır, **`InsertServiceAddress`
atlanır**.
**Görünüme göre farklar:** Card'da kart başına 1–5 kolon seçici (tarayıcıda saklanır), dışa aktarma/
sütun sığdırma/grup paneli/kaydet-sıfırla yok, koşullu biçim yok. Chart'ta arama kutusu görünür,
`AllowSearch` açık metin kolonlarında `contains` ile OR'lanır ve URL `filter`'ının **yerine geçer**; "Seriler"
çekmecesi herkese görünür, kaydetmek `App.Listforms.Listform` ister. Ek filtre metin kutusu ve Chart
araması Enter ile uygulanır. Gantt, Scheduler, Todo ayrıntıları §7.
---
## 6. Pivot
### 6.1 `PivotOptionJson` (`GridPivotOptionDto`)
| Anahtar | Varsayılan |
| --- | --- |
| `ShowFieldPanel` | `true` |
| `AllowFieldDragging` | `true` |
| `ShowDataFields` / `ShowColumnFields` / `ShowRowFields` / `ShowFilterFields` | `true` |
| `ColumnChooserEnabled` | `false` |
| `ShowChart` | `false` |
| `ChartHeight` | `320` |
| `ChartCommonSeriesType` | `"bar"` |
Pivot başka bloklardan da okur: `allowFiltering` ← `FilterRowJson.Visible`, sıralama ← `SortMode != "none"`,
başlık filtresinde "tümünü seç" ← `SelectionJson.AllowSelectAll`, `HeaderFilterJson` boyut/arama,
`PagerOptionJson.ScrollingMode`/`LoadPanel*`, `ColumnOptionJson.ShowBorders/RtlEnabled/HoverStateEnabled`.
Toolbar: "Grafiği göster", filtreyi temizle, kaydet/sıfırla (her zaman), dışa aktar (yalnız `xlsx`,
yalnız `ExportJson.Enabled` — `E` yetkisine bakmaz). Koşullu biçim uygulanır; özel buton çizilmez.
### 6.2 Alan başına `PivotSettingsJson` (`ListFormFieldPivotSettingsDto`)
| Anahtar | Varsayılan | Değerler |
| --- | --- | --- |
| `IsPivot` | `true` | Alan pivotta kullanılsın mı |
| `Area` | `filter` | `row` \| `column` \| `data` \| `filter` |
| `SummaryType` | — | `sum` \| `avg` \| `count` \| `min` \| `max` |
| `GroupInterval` | `""` | `day` \| `dayOfWeek` \| `month` \| `quarter` \| `year` |
| `SortOrder` | `asc` | `asc` \| `desc` |
| `Format` | — | DevExtreme format adı |
| `Expanded` | `false` | Başlangıçta açık |
| `WordWrapEnabled` | `false` | |
Pivot ekranı ayrı bir sorgu istemez; ızgarayla aynı `SelectCommand` üzerinden çalışır. `PivotSettingsJson`'ı
olmayan alan da DTO varsayılanıyla (`IsPivot: true`, `Area: filter`) **alan listesinde görünür** —
pivotta istenmeyen alana `{"IsPivot": false}` yaz.
---
## 7. Ağaç, Gantt, Takvim, Kanban
### 7.1 `TreeOptionJson` (`TreeOptionDto`)
`KeyExpr`*, `ParentIdExpr`*, `HasItemsExpr`, `RootValue` (varsayılan `null`; `""` de `null`
sayılır), `ExpandedRowKeys[]`, `AutoExpandAll` (`false`; seeder yardımcısı
`DefaultTreeOptionJson` `true` yazar), `RecursiveSelection` (`false`).
DTO'daki `TitleExpr`/`StartExpr`/`EndExpr`/`ProgressExpr` ağaçta okunmaz — Gantt alanlarıdır.
### 7.2 `GanttOptionJson` (`GanttOptionDto`)
`KeyExpr`*, `ParentIdExpr`*, `RootValue` + `TitleExpr`*, `StartExpr`*, `EndExpr`*, `ProgressExpr`*
(istemci altısı olmadan görünümü açmaz — `List.tsx` `isLayoutValid`; ağacın `HasItemsExpr`,
`ExpandedRowKeys`, `AutoExpandAll`, `RecursiveSelection` alanları Gantt'ta **okunmaz**), `ScaleType` (varsayılan
`weeks`; DevExtreme `GanttScaleType`: `auto` \| `minutes` \| `hours` \| `sixHours` \| `days` \|
`weeks` \| `months` \| `quarters` \| `years`; kullanıcının seçtiği ölçek grid durumuyla saklanır)
ve düzenleme bayrakları: **`EditingEnabled`** (ana anahtar — `AllowEditing` diye bir alan yoktur,
yazılırsa düşer), `AllowTaskAdding`, `AllowTaskUpdating`, `AllowTaskResourceUpdating`,
`AllowTaskDeleting`, `AllowDependencyAdding`, `AllowDependencyDeleting`, `AllowResourceAdding`,
`AllowResourceUpdating`, `AllowResourceDeleting` (hepsi `false`). Bağımlılık ve kaynak veri kaynağı
olmadığı için `AllowDependency*`/`AllowResource*` **işlevsizdir**; Gantt `PermissionJson` C/U/D'ye bakmaz,
düzenlemeyi yalnız bu bayraklar yönetir. Toolbar: geri al/yinele, aç/kapa, yakınlaştırma, ölçek,
filtre kaydet/sil/temizle, `StateStoringJson.Enabled` ise kaydet/sıfırla (görev listesi genişliği dahil).
### 7.3 `SchedulerOptionJson` (`SchedulerOptionDto`)
| Anahtar | Varsayılan | Anlam |
| --- | --- | --- |
| `TextExpr`* · `StartDateExpr`* · `EndDateExpr`* | — | Başlık ve zaman kolonları |
| `UserNameExpr` · `DescriptionExpr` | — | Randevu sahibinin kullanıcı adı (kartta avatarı çizilir) ve açıklama |
| `AllDayExpr` | — | Tüm gün bayrağı kolonu |
| `RecurrenceRuleExpr` · `RecurrenceExceptionExpr` | — | Tekrar kuralı kolonları |
| `StartDayHour` / `EndDayHour` | `8` / `20` | Görünen saat aralığı (`0` geçerlidir) |
| `DefaultView` | `"week"` | Ekranın çizdiği sekiz görünüm: `day` \| `week` \| `workWeek` \| `month` \| `timelineDay` \| `timelineWeek` \| `timelineMonth` \| `agenda` |
| `ShowAllDayPanel` | `true` | |
| `CellDuration` | `30` | Hücre dakikası |
| `FirstDayOfWeek` | `1` | 0 = Pazar |
| `CrossScrollingEnabled` | `false` | Kaynak (resource) görünümünde yatay+dikey kaydırma |
| `AllowAdding` / `AllowEditing` / `AllowDeleting` / `AllowDragging` / `AllowResizing` | `false` | |
> **`Resources[]` bugün çalışmaz.** UI modeli (`proxy/form/models.ts`) ve `SchedulerView`
> randevuları renklendiren kaynak eşlemesini (`FieldExpr`, `DataSource[]`, `Label`,
> `UseColorAsDefault`) okur, ama C# `SchedulerOptionDto`'da alan yoktur — girişteki kural gereği
> sunucu anahtarı düşürür, seed'e yazılan değer istemciye hiç ulaşmaz. DTO'ya alan eklemek Core'dur
> (`lowcode` §0.8); kaynağa göre renk ayrımı bugün kurulamaz, ihtiyaç "Core gerektiren kısım" notudur.
Scheduler'da not, iş akışı ve özel buton yoktur; düzenleme yalnız `SchedulerOptionJson.Allow*` ile yönetilir,
`PermissionJson`'a bakmaz. Koşullu biçim uygulanmaz.
### 7.4 `TodoOptionJson` (`TodoOptionDto`) — Kanban
`TitleExpr`*, `StatusExpr`*, `DescriptionExpr`, `AnalysisExpr` (detayda Açıklama yanında HTML sekmesi), `DueDateExpr`, `TagExpr`, `AssigneeExpr`,
`PriorityExpr`, `CompletedExpr`, `OrderExpr`, `TimesheetExpr`, `SubTaskExpr`,
`StatusOrder` (kolon sırası, virgüllü), `AllowDragging` (varsayılan `true`).
- En fazla **1000 kayıt** yükler. Panodan kolon eklenir/yeniden adlandırılır; yeniden adlandırma bütün
satırları günceller ve `UpdateServiceAddress` ister. Kolon düzeni `todo-` durumuna otomatik kaydedilir
(`StateStoringJson.SavingTimeout` debounce).
- Ekle/düzenle/sil yalnız `EditingOptionJson` + servis adreslerine bakar, `PermissionJson`'a bakmaz.
Toolbar'da yalnız `Url`/`OnClick` tipli özel butonlar çizilir. `backlog`, `in progress` gibi bazı durum
adları otomatik çevrilir.
- Detay ekranında Ctrl/Cmd+S kaydeder, Esc sürüklemeyi iptal eder.
Panonun yüksekliği `TodoOptionJson` ile değil ekranın `Height` kolonuyla belirlenir; boş
bırakıldığında pano sayfanın kalan yüksekliğini kaplar ve her kolon kendi içinde kayar.
> `*` işaretli alan boşsa seeder o görünümün JSON'unu **yazmaz**; bayrağı açmak yetmez.
---
## 8. Chart (grafik) sözleşmesi
Grafik, ListForm üzerindeki **21 JSON kolonuyla** yapılandırılır. Hepsi DevExtreme
`dxChart` yapılandırmasının karşılık gelen bölümüne geçer — ama tipli DTO'dan süzülerek
(girişteki "bilinmeyen anahtar düşer" kuralı grafik kolonları için de geçerlidir). Entity'deki
22. kolon `DataSourceJson` hiçbir yerde okunmaz (§8.2).
Grafik iki yoldan açılır: ekranın `LayoutJson.Chart` bayrağıyla bir görünüm olarak, ya da
`ListFormType = "Chart"` ile yalnız grafikten oluşan bir ekran (`/admin/chart/{kod}`) olarak.
| Kolon | Karşılığı |
| --- | --- |
| `SeriesJson` | `series[]` — **zorunlu**, §8.1 |
| `CommonJson` | Grafik geneli (palet, zemin), §8.3 |
| `CommonSeriesSettingsJson` | Tüm serilere ortak ayarlar — `ChartSeriesDto`'nun `Name`/`SummaryType`/`UserId` dışındaki alanları |
| `ArgumentAxisJson` · `ValueAxisJson` (**dizi**) · `CommonAxisSettingsJson` | Eksenler, §8.5 |
| `LegendJson` | Açıklama kutusu, §8.6 |
| `TooltipJson` | İpucu balonu, §8.7 |
| `PanesJson` · `CommonPaneSettingsJson` | Bölmeler (çok panelli grafik) |
| `AnnotationsJson` · `CommonAnnotationsSettingsJson` | Ek açıklamalar |
| `CrosshairJson` | Artı imleç çizgileri |
| `ScrollBarJson` | Kaydırma çubuğu |
| `ZoomAndPanJson` | Yakınlaştırma/kaydırma |
| `SizeJson` · `MarginJson` · `AdaptiveLayoutJson` | Boyut, kenar boşluğu, dar ekran davranışı |
| `TitleJson` | Başlık/alt başlık |
| `AnimationJson` | Animasyon |
| `ExportJson` | Dışa aktarma/yazdırma |
Ekranın `UserId`/`RoleId` varyantı **okunmaz** (§5.7); kullanıcıya özel grafik yalnız seri bazında
`ChartSeriesDto.UserId` ile verilir.
### 8.1 `SeriesJson` (`ChartSeriesDto[]`)
| Anahtar | Varsayılan | Anlam |
| --- | --- | --- |
| `Name` | — | Açıklamada görünen seri adı |
| `Type` | — (DevExtreme: `line`) | Değer küçük harfli `value`'dur (`fullstackedbar`); etiketle (`"Fullstacked Bar"`) kaydedilmiş eski seri çizimde değere çevrilir. Seçenekler: `line` \| `stepline` \| `spline` \| `bar` \| `area` \| `splinearea` \| `steparea` \| `scatter` \| `bubble` \| `rangebar` \| `rangearea` \| `candlestick` \| `stock` \| `stackedbar` \| `stackedarea` \| `stackedline` \| `stackedspline` \| `stackedsplinearea` \| `fullstackedbar` \| `fullstackedarea` \| `fullstackedline` \| `fullstackedspline` \| `fullstackedsplinearea` (ListForm editörünün listesi: `chartSeriesTypeOptions`) |
| `ArgumentField` | `"arg"` | X ekseni kolonu — ekranın sorgusundaki bir **alan adı** |
| `ValueField` | — | Y ekseni kolonu |
| `SummaryType` | `"sum"` (yalnız DTO) | `sum` \| `avg` \| `count` \| `min` \| `max` — özetsiz seri gruplu istekte değer üretmez (§8.2) |
| `Color` | — | Seri rengi |
| `Axis` | — | Hangi değer ekseni (`ValueAxisJson[].Name`) |
| `Visible` | `true` | |
| `ShowInLegend` | `true` | |
| `Width` | `2` | Çizgi kalınlığı |
| `DashStyle` | `"solid"` | `solid` \| `dash` \| `dot` \| `longDash` |
| `Pane` · `BarWidth` · `BarPadding` · `BarOverlapGroup` · `CornerRadius` · `IgnoreEmptyPoints` · `SelectionMode` · `RangeValue1Field`/`RangeValue2Field` | — | **Seri bazında okunmaz** (`buildSeriesDto` seriye geçirmez) — yalnız `CommonSeriesSettingsJson` üzerinden etkilidir |
| `Label` | — | `ChartLabelDto`: `Visible` (`false`), `Format`, `Font`, `BackgroundColor` (`#f05b41`). `CustomizeText` metin olarak geçer, DevExtreme fonksiyon beklediği için etkisizdir |
| `UserId` | — | Kullanıcı adı (`UserName`, kimlik değil). O kullanıcının kendi serisi varsa **yalnızca** onun serileri çizilir, yoksa `UserId`'si ne olursa olsun bütün seriler. Kullanıcı grafik çekmecesinden kendi serisini kaydettiğinde bu alan dolar |
### 8.2 Veri — grafik ekranın kendi sorgusundan beslenir
İstemci grafiği ekranın `SelectCommand`'ı (ve `DefaultFilter`, kırılım
filtreleri, kullanıcının o anki filtresi) üzerinde, serilerin `ArgumentField` değerleriyle
**gruplanmış** ve her serinin `ValueField` + `SummaryType` çiftiyle **özetlenmiş** bir istek atılır
(`useListFormCustomDataSource`, `layoutTypes.chart`). Yani `SELECT … GROUP BY` yazmaya gerek yoktur;
özetlenecek ham satırları döndüren sorgu yeter. Farklı kırılımda ikinci bir grafik gerekiyorsa
ikinci bir ListForm (ya da view) açılır. Birden çok farklı `ArgumentField` iç içe gruplanır; özet
değerleri **seri sırasına göre** eşlenir — `ValueField`/`SummaryType` eksik bir seri sonrakileri kaydırır,
aynı `ValueField` iki seride farklı özetle kullanılırsa sonuncusu kazanır.
Özet, liste sorgusunun grup yolundan (`GroupQuery`) üretilir; iki sağlayıcıda da aynı çalışır. Ayrı bir
grafik sorgusu yoktur.
`DataSourceJson` kolonu ve `ChartDataSourceDto { Query }` / `ChartDataRequestDto` tipleri kodda
durur ama **okunmaz** — oraya sorgu yazmak grafiği değiştirmez.
### 8.3 `CommonJson` (`ChartCommonDto`)
| Anahtar | Varsayılan | Anlam |
| --- | --- | --- |
| `Palette` | `"Material"` | `Bright` \| `Harmony Light` \| `Ocean` \| `Pastel` \| `Soft` \| `Soft Pastel` \| `Vintage` \| `Violet` \| `Carmine` \| `Dark Moon` \| `Dark Violet` \| `Green Mist` \| `Soft Blue` \| `Material` \| `Office` |
| `PaletteExtensionMode` | `"blend"` | `alternate` \| `blend` \| `extrapolate` |
| `Theme` | `"generic.light"` | DTO'da durur ama `Chart.tsx` grafiğe geçirmez — tema uygulamanın temasından gelir |
| `ContainerBackgroundColor` | `"#FFFFFF"` | |
| `AdjustOnZoom` | `true` | |
| `DefaultPane` | — | |
| `Disabled` | `false` | |
### 8.4 Boyut ve yerleşim
- `SizeJson` (`ChartSizeDto`): `UseSize` (`false`), `Width` (`400`), `Height` (`200`).
`UseSize: false` iken grafik pencereye yayılır (`Width`/`Height` yok sayılır).
- `MarginJson` (`ChartMarginDto`): `Top`, `Bottom`, `Left`, `Right`.
- `AdaptiveLayoutJson` (`ChartAdaptivelayoutDto`): `Width`/`Height` eşiği (`80`/`80`),
`KeepLabels` (`true`).
- `TitleJson` (`ChartTitleDto`): `Text`, `Subtitle`, `HorizontalAlignment` (`center`),
`VerticalAlignment` (`top`), `TextOverflow` (`ellipsis`), `WordWrap`.
- `AnimationJson` (`ChartAnimationDto`): `Enabled` (`true`), `Duration` (`1000`),
`Easing` (`easeOutCubic` \| `linear`), `MaxPointCountSupported` (`300`).
- `ExportJson` (`ChartExportDto`): `Enabled`, `PrintingEnabled` (`true`),
`AllowExportSelectedData`, `BackgroundColor` (`#ffffff`), `Margin` (`10`).
### 8.5 Eksenler
`ArgumentAxisJson` (`ChartArgumentAxisDto`): `Title`, `Visible` (`true`), `Width` (`1`),
`ArgumentType` (— \| `datetime` \| `numeric` \| `string`),
`AggregationInterval` (`millisecond` \| `second` \| `minute` \| `hour` \| `day` \| `week` \| `month` \| `quarter` \| `year`),
`WorkdaysOnly` (`false`), `Position` (`bottom` \| `left` \| `right` \| `top`),
`HoverMode` (`none` \| `allArgumentPoints`), `Grid` (`ChartAxisGridDto`: `Visible`, `Color`
`#d3d3d3`, `Width` `1`), `Label` → `DisplayMode` (`standard` \| `rotate` \| `stagger`),
`RotationAngle` (`90`), `OverlappingBehavior` (`rotate` \| `stagger` \| `none` \| `hide`). Argüman alanında
lookup varsa etiket metni lookup'ın görünen değerine otomatik çevrilir.
`ValueAxisJson` bir **dizidir** (`ChartValueAxisDto[]`; çok eksenli grafik — seri `Axis` alanıyla
`Name`'e bağlanır): `Name`, `Title`, `ValueType` (`numeric`), `Position` (`left`),
`Type` (`continuous` \| `discrete` \| `logarithmic`), `Visible` (`true`), `Width` (`1`), `Grid`,
`Breaks[]` (`StartValue`/`EndValue`), `BreakStyle` (`Color` `#ababab`, `Line` `waved`, `Width` `5`),
`AutoBreaksEnabled` (`true`), `MaxAutoBreakCount` (`2`). Tek nesne yazmak `JsonException` verir.
`CommonAxisSettingsJson` (`ChartCommonAxisDto`: `Title`, `Visible`, `Width`, `Grid`) iki eksene
birden uygulanır; **eksene özel ayar varsa yok sayılır.** `ArgumentAxisJson` sunucudan her zaman dolu
döndüğü için argüman ekseninde fiilen hiç etkili olmaz; değer ekseninde yalnız `ValueAxisJson` boş (`[]`)
iken etkilidir. `ChartAxisGridDto.Visible` varsayılanı `false`'tur.
### 8.6 `LegendJson` (`ChartLegendDto`)
`Visible` (`true`), `Position` (`outside` \| `inside`), `Orientation` (`vertical` \| `horizontal`),
`ColumnCount` (`0`), `RowCount` (`0`), `Title`, `BackgroundColor`, `Border`.
### 8.7 `TooltipJson` (`ChartTooltipDto`)
`Enabled` (**`false`** — `TooltipJson` boşsa ipucu kapalıdır; açmak için `{"Enabled": true}` yaz), `Shared`,
`Format` / `ArgumentFormat` (DevExtreme format adları),
`Color` (`#ffffff`), `ArrowLength` (`10`), `Location` (`center` \| `edge`),
`PaddingLeftRight` (`18`), `PaddingTopBottom` (`15`), `Border`, `Font` (ipucu fontunun rengi `#000000`).
### 8.8 Diğerleri
- `PanesJson` (`ChartPanesDto[]`): `Name`, `Height` (`250`), `BackgroundColor`.
Seriler `Pane` alanıyla bir bölmeye bağlanır.
- `CrosshairJson` (`ChartCrosshairDto`): `Enabled` (`false`), `Color` (`#f05b41`), `Width` (`1`),
`DashStyle` (`dot`), `HorizontalLine`/`VerticalLine` (`ChartCrosshairLineDto`: `Color` `#f05b41`,
`DashStyle` `dot`, `Visible` `true`, `Width` `1`, `Label`), `Label`.
- `ScrollBarJson` (`ChartScrollBarDto`): `Visible` (`false`), `Position` (`top`), `Color` (`gray`),
`Width` (`10`), `Offset` (`5`).
- `ZoomAndPanJson` (`ChartZoomAndPanDto`): `ArgumentAxis` / `ValueAxis`
(`none` \| `pan` \| `zoom` \| `both`), `AllowMouseWheel` (`true`), `AllowTouchGestures` (`true`),
`DragToZoom` (`false`), `PanKey` (`shift`), `DragBoxStyle` (`Color`, `Opacity` `0`).
- `AnnotationsJson` (`ChartAnnotationDto[]`): `Name`, `Type` (`text` \| `image`), `Text`, `Image`,
konum için `Series` + `Argument`/`Value` ya da piksel `X`/`Y`, `OffsetX`/`OffsetY` (`-1`),
`Width` (`0`)/`Height` (`-1`), `PaddingLeftRight`/`PaddingTopBottom` (`10`), `WordWrap` (`normal`),
`Color` (`#ffffff`), `Font`, `Border`, `TooltipEnabled` (`true`), `Description`.
`CommonAnnotationsSettingsJson` (`ChartCommonAnnotationDto`) aynı alanları `Name` olmadan taşır.
- `CommonPaneSettingsJson` (`ChartCommonPaneDto`): yalnızca `BackgroundColor`.
- Ortak alt nesneler: `Font` (`Color` `#FFFFFF`, `Family` `"Segoe UI", "Helvetica Neue", "Trebuchet MS",
Verdana, sans-serif`, `Size` `12`, `Weight` `400`), `Border` (`Visible` `false`, `Color` `#d3d3d3`,
`Width` `1`, `CornerRadius` `0`, `DashStyle` `solid`).
- `ExportJson.Enabled` varsayılanı `false`'tur ve Grid/Pivot dışa aktarmayı da açar (§1, §5.8).
`ChartOptionsRequestDto` kodda durur, kullanılmaz.
---
## 9. Alan (`ListFormField`) JSON kolonları
`lowcode` §4.3.4–4.3.7'de anlatılan `LookupJson`, `ValidationRuleJson`, `ColumnStylingJson`
dışında kalanlar:
### 9.1 `ColumnFilterJson` (`ColumnFilterDto`)
| Anahtar | Anlam |
| --- | --- |
| `AllowFiltering` | Bu sütun filtrelenebilsin mi (`true`) |
| `FilterOperations[]` | **Ölü ayar** — istemci okumaz; operatörler DevExtreme'in tipe göre listesidir. DTO varsayılanı tipe göre: metin `contains, notcontains, startswith, endswith, =, <>`; sayı/tarih `=, <>, <, >, <=, >=, between` |
| `SelectedFilterOperation` | Varsayılan seçili operatör |
| `FilterValue` | Ekran açılırken uygulanan filtre değeri |
### 9.2 `ColumnHeaderJson` (`ColumnHeaderDto`)
`AllowHeaderFiltering` (`true`), `AllowSearch` (`false`) ve **özel başlık filtresi listesi**
(`ColumnsHeaderDataSourceDto`: `Text` + `Value` — DevExtreme filtre ifadesi dizisi):
```json
{ "AllowHeaderFiltering": true,
"DataSource": [ { "Text": "3000 altı", "Value": ["Amount", "<", 3000] },
{ "Text": "3000 üstü", "Value": ["Amount", ">=", 3000] } ] }
```
Anahtarlar PascalCase yazılır; `text`/`value` küçük harfle yazılırsa liste boş gelir (giriş, ortak
kurallar). Eski `DataSourceString` alanı (aynı listenin metin hâli, `Obsolete`) getter'ı olduğu için JSON'a
da yazılır; okunmaz, yazma. Ekranın `HeaderFilterJson.Visible` bayrağı kapalıysa bu liste de görünmez.
### 9.3 `GroupingJson` (`ColumnGroupingDto`)
`AllowGrouping` (`true`), `GroupIndex` (kaçıncı seviyeden gruplanacağı), `AutoExpandGroup` (`false`).
`GroupIndex` verilen sütun ekran açılışında gruplanmış gelir; istemci `if (groupIndex)` kullandığı için
**`0` yok sayılır** — ilk seviye `1`'dir.
### 9.4 `ColumnCustomizationJson` (`ColumnCustomizationDto`)
`Fixed` (`false`), `FixedPosition` (`left` \| `right`), `AllowReordering` (`false`; wizard `true` yazar).
Sol/sağa sabitlenmiş kolon yatay kaydırmada sabit kalır. Kolon başına `AllowReordering` yalnızca ekranın
`ColumnOptionJson.AllowColumnReordering`'i açıksa etkilidir.
### 9.5 `TotalSummaryJson` / `GroupSummaryJson` (`ColumnTotalSummaryDto`)
| Anahtar | Anlam |
| --- | --- |
| `SummaryType` | `sum` \| `avg` \| `count` \| `min` \| `max` — sunucu yalnızca bu beşini kabul eder (`SelectQueryManager.SummaryTypes`). C# enum'u yoktur; TS `SummaryTypeEnum`'unda ayrıca `none` vardır. `custom` hesaplayıcısı bağlanmadığı için kullanılmaz |
| `ValueFormat` | DevExtreme format adı (`fixedPoint`, `currency`, `percent`, `shortDate`…) |
| `DisplayFormat` | Metin şablonu: `{0}` biçimlenmiş değer, `{1}` `ShowInColumn`'daki sütunun başlığı |
| `ShowInColumn` | Özetin hangi sütunun altında görüneceği (kolon adı). `DisplayFormat`'taki `{1}` bunun başlığıdır |
| `ShowInGroupFooter` · `AlignByColumn` | **Ölü ayar** — yönetim formunda düzenlenir ama grid'in grup özetine (`Grid.tsx`) aktarılmaz; grup özeti her zaman grup başlığının yanında çıkar |
`TotalSummaryJson` listenin altındaki toplam satırını, `GroupSummaryJson` grup kırılımındaki
özeti üretir; ikisi de **aynı DTO'yu** kullanır, fark yazıldıkları kolondur. Özetler yalnızca
**Grid** görünümünde çizilir; Tree ve Card yok sayar.
"Listenin altında toplam görünsün" isteği budur; ayrı sorgu ya da widget gerekmez.
### 9.6 `JoinTableJson` (`JoinTableDto`)
Bir kolonu **başka bir tablodan** çekmek için; view yazmadan tek alanlık join kurar.
| Anahtar | Anlam |
| --- | --- |
| `JoinType` | `INNER JOIN` · `LEFT JOIN` · `RIGHT JOIN` — kısaltma (`LEFT`, `INNER`) sorguda tam kalıba çevrilir, tanınmayan değer `LEFT JOIN` olur (`JoinTable.JoinClause`) |
| `TableName` | Join kurulacak tablo |
| `BaseTableJoinFieldName` | Ana tablonun join alanı |
| `JoinFieldName` | Join kurulan tablonun ilişkili alanı |
| `SelectFieldName` | Ekranda gösterilecek alan |
| `FieldNameAlias` | Alanın takma adı — doluysa kolon sorgudan **bu adla** döner; grid sütunu `FieldName`'e bağlı olduğu için boş kalır. Ekranda gösterilecek join alanında boş bırak |
| `JoinCondition2` | İkinci koşulun bağlacı (`AND` varsayılan \| `OR`) |
| `JoinFieldName2` | İkinci koşulun alanı: `Def_T_City.CultureName` ya da yalın `CultureName` (join tablosuna bağlanır); parçalar ayrı ayrı tırnaklanır |
| `BaseTableJoinFieldName2_OrValue` | İkinci koşulun karşı tarafı: **ham** yazılır (tırnaklanmaz) — tablo adlı alan kendi tırnağıyla, sabit `'en'` gibi |
Aynı JOIN cümlesi birden çok alanda geçerse bir kez yazılır; join alanına filtre/sıralama
`"Tablo"."SelectFieldName"`'e çevrilir. Üç-dört join'i geçen ihtiyaçta view tasarımcısı tercih edilir (`lowcode` §8.2).
### 9.7 Diğer alan kolonları
| Kolon | Varsayılan | Anlam |
| --- | --- | --- |
| `FieldName` | — | Kaynaktaki sütun adı; SQL'deki adıyla birebir. **`Kolon:Özellik`** biçimi (`Options:TemplateName`) JSON metin kolonunun tek özelliğini ayrı form alanı yapar: okurken JSON'dan doldurulur, kayıtta tek JSON'a birleştirilir; etiket özellik adıdır, yetki kök kolondan okunur |
| `CaptionName` | — | Sütun başlığı — **dil anahtarı** |
| `PlaceHolder` | — | Editörün placeholder metni — **dil anahtarı**; yalnızca düzenleme popup'ı, form sayfası ve Card'da görünür |
| `Alignment` | `"left"` | `left` \| `center` \| `right` |
| `Format` | — | Hücre biçimi (DevExtreme format adı ya da desen); `EditorOptions.displayFormat`, sonra `EditorOptions.format` varsa onlar önce gelir |
| `BandName` | — | Sütunu bir başlık bandının altına toplar; `Üst:Alt` ile en çok iki seviye. Band başlığı **düz metindir**, çevrilmez |
| `Width` · `ListOrderNo` | — | Genişlik (`0` = otomatik, wizard varsayılanı; yalnızca `>0` uygulanır) ve grid'deki sıra |
| `Visible` | — | Sütun grid'de görünür mü; `false` ise kolon seçicide durur ama **sorguya girer**. Entity'de `bool?`, DTO'da `bool` — her zaman açıkça yaz. Formda olup listede görünmeyen alan gizli grid kolonu olarak eklenir |
| `IsActive` | `true` | `false` **ya da boş** olan alan sorguya da arayüze de girmez |
| `SortIndex` · `SortDirection` | — | Açılıştaki sıralama (`asc` \| `desc`); sunucu yalnızca `SortIndex > 0` alanları artan sırayla kullanır (`0`/boş sıralamaya girmez) |
| `AllowSearch` · `AllowEditing` · `AllowAdding` | `true` | Arama paneline dâhil olma (DTO varsayılanı `false` — `AllowSearch`'ü açıkça yaz). `AllowEditing`/`AllowAdding` = `false` alanı o modda formda **gizler** ve payload'dan atar (salt okunur yapmaz; salt okunurluk `EditorOptions.readOnly` ya da alan yetkisinin `U`/`C` eksikliğidir). Grid kolonunda `allowEditing = AllowEditing \|\| AllowAdding` |
| `ColumnCssClass` · `ColumnCssValue` | — | Koşulsuz stil: sınıf adı + **tam CSS kuralı** (`.sinif{color:red}`) ya da `http…`/`/…` ile başlayan stylesheet adresi; `ColumnCssValue` yalnızca `ColumnCssClass` doluysa eklenir. Koşullu olan `ColumnStylingJson` |
| `SourceDbType` | `0` (entity) · `16` (wizard) | Kolonun `System.Data.DbType` karşılığı. İstemci tipi (`DataType`) buradan türetilir: metin/Guid → `string`; Currency/Decimal/Double/Int*/Single/UInt* → `number`; Date → `date`; DateTime/DateTime2/DateTimeOffset → `datetime`; Boolean → `boolean`. **Byte/SByte/Binary/Time/Xml/VarNumeric boş tip verir** — `tinyint` kolonunu `Int16` olarak yaz ki sayı filtresi/biçimi çalışsın |
| `EditorType2` · `EditorOptions` · `EditorScript` | — | Editör tipi ve davranışı (`lowcode` §5, §6). Liste etkisi: `dxHtmlEditor` `encodeHtml=false`, lookup'sız `dxColorBox` renk kutusu, `dxRating` sola hizalı; `EditorOptions.encodeHtml` kolon ayarını ezer |
| `ColSpan` · `EditOrderNo` · `EditGroupOrderNo` | — | Düzenleme formundaki yeri; `EditGroupOrderNo` `EditingFormJson`'daki `Order` ile eşleşir. Alanın forma girip girmediğine **`EditOrderNo`** karar verir (boşsa girmez); `EditGroupOrderNo` boşsa 1. gruba düşer |
| `PermissionJson` | — | Alan yetkileri (§5.1) |
| `CultureName` · `UserId` · `RoleId` | — | Alan varyantı (§5.7) |
> **`DefaultValue` / `DefaultValueType` / `ReadOnly` diye bir alan kolonu yoktur.** İstemciye
> giden `ColumnFormatDto`'da bu adlar görünür ama çıktı alanlarıdır; seed'e yazılan değer düşer.
> Varsayılan değerler ekran seviyesindeki `Insert/Update/Delete/FormFieldsDefaultValueJson`
> bloklarında, alan adıyla eşleşen `FieldsDefaultValueDto` satırları olarak durur
> (`lowcode` §4.3.5); salt okunurluk ise `EditorOptions.readOnly` ya da `EditorScript`'in
> `readOnly` tarifidir (`lowcode` §6.6).
### 9.8 Tipli editör seçenekleri
`EditorOptions` JSON'unun bazı anahtarları backend'de **tipli DTO'ya** çözülür; yanlış tip
sessizce yok sayılır. Bu çözme, ekran kolonlarının aksine **büyük/küçük harfe duyarsızdır**
(`columns` ile `Columns` aynıdır):
| DTO | Hangi `EditorType2` | Anahtarlar (varsayılan) |
| --- | --- | --- |
| `GridBoxOptionsDto` | `dxGridBox` | `Columns[]` (ilk kolon `Key` ile aynı adda olmalı), `SelectionMode` (`"single"`), `FilterRowVisible` (`false`), `AcceptCustomValue` (`false`), `ShowClearButton` (`true`), `Height` (`250`), `Width` (`500`) |
| `TagBoxOptionsDto` | `dxTagBox` | `ShowSelectionControls` (`true`), `MaxDisplayedTags` (`3`), `ShowMultiTagOnly` (`false`), `ApplyValueMode` (`"useButtons"`), `SearchEnabled` (`true`), `AcceptCustomValue` (`false`), `ShowClearButton` (`true`) — değer tek kolonda `\|` ile saklanır, bkz. §4.3 |
| `ImageUploadOptionsDto` | `dxImageUpload` · `dxImageViewer` | `UploadUrl`, `Accept`, `Multiple`, `MaxFileSize` (bayt), `Width`, `Height`, `Capture` (`environment`/`user` — mobilde dosya seçici yerine **yalnızca** kamerayı açar; boş bırakılırsa dokunmatik cihazda dosya seçicinin yanına ayrı bir kamera butonu gelir) — hepsi nullable, varsayılanı yok |
Çözüm **yalnızca alanın `EditorType2` değeri eşleşiyorsa** yapılır: `dxTextBox` bir alana
`columns` yazmak `GridBoxOptions`'ı doldurmaz. Ayrıca `ListFormField.EditorOptions`'ın ham
metni istemciye **her hâlükârda** gider; tipli DTO'lar onun yanında ek bir kolaylıktır ve
`JsonException` durumunda sessizce `null` döner — yani bozuk JSON burada ekranı düşürmez, ama
tipli okumaya bağlı davranış (GridBox kolonları, TagBox etiket sayısı) çalışmaz.
**Cihaz yetenekleri yeni editör tipi istemez** (üçü de HTTPS ister):
| İhtiyaç | Nerede |
| --- | --- |
| Fotoğraf çekme | `dxImageUpload` · `Capture` (yukarıda) |
| Görsel tutan kolon | Editör `dxImageUpload`, kolon `nvarchar(MAX)` (base64); `dxImageViewer` kolona bağlanmaz (`lowcode.instructions.md` §0.12 madde 5) |
| Alana barkod/QR okutma | `dxTextBox` `EditorOptions.buttons[]` içinde `onClick: "@SCAN_BARCODE"` (`lowcode` §6.4) |
| Sürekli okutup her kodu yeni satır yapma | Toolbar butonu `DialogName: "BarcodeScanDialog"` + `DialogParameters` `{"field": "…", …sabit alanlar}` — ekranın insert ucundan geçer (`lowcode` §4.9) |
| Cihaz konumu | `EditorScript` `location` tarifi → `geoTo('Enlem', 'Boylam')` (`lowcode` §5.3) |
| Tasarımcıda okutma / kod basma | `BarcodeScanField` · `BarcodeViewer` (§10.9) |
---
## 10. Visual Designer envanteri
`lowcode` §7 tasarımcı dokümanının şemasını anlatır; burada **kutuda ne olduğu** listelenir.
### 10.1 Toolbox aileleri
Kutunun içeriği tek bir yerden, `getDesignerCatalog()` (`components/visualDesigner/catalog.ts`)
tarafından üretilir: elle yazılmış tanımlar + `components/ui` altındaki bileşenlerden
otomatik çıkarılan metadata (`generated/componentProps.json`).
| Aile | Bileşenler |
| --- | --- |
| `layout` | `PageContainer` (maxWidth, padding, gap, className), `FlexRow` (columns, firstColumnWidth, gap, wrap, align, className), `Table` |
| `data` | `Form` (§10.2) ve veri bağlanabilen UI bileşenleri: `Select`, `AutoComplete`, `Dropdown`, `Menu`, `Pagination`, `Radio.Group`, `Tabs`, `Grid` |
| `platform` | Gömülü ListForm görünümleri: `ListView`, `DataGridView`, `TreeView`, `GanttView`, `TodoBoard`, `CardView`, `SchedulerView`, `PivotView`, `ChartView` (§10.7). Public site düğümleri: `PublicHeaderBar`, `PublicNav`, `PublicMobileMenu`, `PublicLogo`, `PublicThemeToggle`, `PublicLanguageSelector`, `PublicDemoButton` (`PUBLIC_SITE_COMPONENTS`, §10.7) |
| `html` | `a`, `div`, `p`, `span`, `h1`, `h2`, `h3`, `h4`, `h5`, `img` — **yalnız bu on etiket** |
| `ui` | `Avatar`, `Badge`, `BarcodeScanField`, `BarcodeViewer`, `Button`, `Calendar`, `Card`, `Checkbox`, `Chips`, `ColorPicker`, `DatePicker`, `ImageViewer`, `Input`, `Knob`, `Marquee`, `Notification`, `PlatformIcon`, `Progress`, `Radio`, `RangeCalendar`, `Rate`, `ScrollBar`, `Skeleton`, `Slider`, `Spacer`, `Spinner`, `Switcher`, `Tag`, `TimeInput`, `Tooltip`, `Upload` |
| `custom` | Kayıtlı diğer custom component'ler (`Dependencies`'e eklenir) |
**`input`, `button`, `textarea`, `select`, `checkbox` HTML etiketleri kutuda yoktur**
(`HTML_UI_DUPLICATES`): karşılıkları `ui` ailesindeki `Input`, `Button`, `Select`, `Checkbox`
bileşenleridir; ham etiket bırakmaya çalışma, tema ve doğrulama onlardan gelir.
Kutu dışında tutulanlar (`EXCLUDED_COMPONENTS`): `Alert`, `Breadcrumb`, `Dialog`, `Drawer`,
`FormContainer`, `FormItem`, `InputGroup`, `MenuItem`, `Segment`, `Steps`. Bunlara ihtiyaç
duyan bir yerleşim, tasarımcı yerine **Code modunda** yazılır (`lowcode` §7.1.1) — orada
`components/ui`'nin tamamı kapsamdadır.
Çocuk düğüm kabul edenler: `PageContainer`, `FlexRow`, `Form`, `div`, `a`, `Card`, `Table`, `Tabs`,
`PublicHeaderBar`, `PublicMobileMenu` ve **her custom component**. Diğer düğümlerin içine bırakma; kanvas kabul etse de üretilen kod
çocukları çizmez.
Düğüm başına iki ortak prop, hangi ailede olursa olsun geçerlidir:
| Prop | Anlam |
| --- | --- |
| `designerPermission` | Boş değilse düğüm yalnızca bu yetki verilmişse **çizilir**; verisini göremeyeceği bir grid hiç oluşturulmaz |
| `className` | Tailwind sınıfları |
> **Önce yeniden kullan:** İstenen şey bir ListForm ekranının verdiği liste/ağaç/pano/grafikse
> primitiflerden kurma; `platform` düğümünü `listFormCode` ile bırak.
>
> **Eksik olan = Core, komutla eklenir:** İstenen komponent bu envanterle büyük ölçüde kuruluyor
> ama bir bileşende **olay, prop ya da küçük bir yetenek** eksikse komponent kurulabildiği kadar
> üretilir, eksik parça son mesajda "Core gerektiren kısım" notuyla bildirilir. Kullanıcı
> **"core sisteme ekle"** derse parça `components/ui/{Name}`'e geriye dönük uyumlu eklenir,
> `componentProps.json` elle güncellenir, olaysa `catalog.ts` `DESIGNER_PRIMARY_EVENTS`,
> değer kümesiyse `NAMED_UNION_OPTIONS`/`PROPERTY_OVERRIDES`, sonra §10.9/§10.10 tabloları
> tamamlanır (`lowcode` §7.3 notu). Yeni `ui` bileşeni, yeni toolbox ailesi, Developer Kit
> menüsüne yeni komponent ya da var olan bileşenin davranışını değiştiren ekleme komut olmadan
> **yapılmaz** (`lowcode` §0.8).
### 10.2 `Form` düğümü
Dört CRUD ucunu sahiplenen kapsayıcı. İçine bırakılan her bileşen kaydın bir kolonuna bağlanır.
| Prop | Varsayılan | Anlam |
| --- | --- | --- |
| `title` | `""` | Kart başlığı; boşsa Card başlığı hiç çizilmez (`::` önekli değer dil anahtarıdır) |
| `selectEndpoint` / `insertEndpoint` / `updateEndpoint` / `deleteEndpoint` | — | GET / POST / PUT / DELETE uçları |
| `selectPermission` / `insertPermission` / `updatePermission` / `deletePermission` | — | Komut yetkileri; boşsa dokümanın `permissionCode` değeri + `""`/`.Create`/`.Update`/`.Delete` **otomatik** çözülür (`FORM_AUTO_SUFFIXES`), o da yoksa komut açıktır |
| `keyFieldName` | `"id"` | Anahtar kolon |
| `collectionPath` | `""` | Select yanıtındaki liste yolu; boşsa yanıtın kendisi |
| `keySource` | `"query"` | `query` \| `route` — anahtarın URL'de nereden okunacağı |
| `keyParamName` | `""` | Parametre/segment adı; boşsa `route`'ta son segment kullanılır |
| `previewKeyValue` | `""` | Yalnızca tasarımcı önizlemesi; üretilen koda girmez |
| `autoLoad` | `true` | Açılışta veriyi çek |
| `showToolbar` | `true` | Kaydet/Sil/Gezinme çubuğu |
| `columnCount` | `1` | Form içindeki alanların kaç kolona diziliceği |
| `gap` | `16` | Alanlar arası boşluk (px) |
| `className` | `""` | Ek sınıflar |
Olayları ve script API'si `lowcode` §7.8'dedir. Bir sayfada birden fazla `Form` olabilir;
ana-detay için detay tarafının veri kaynağına `source: "record"` filtresi verilir ve değeri
`<anaFormRef>.<kolon>` biçiminde yazılır (§10.4).
### 10.3 Veri bileşenleri
`DESIGNER_DATA_COMPONENTS` iki tür tanır:
| Tür | Bileşenler | Koleksiyon prop'u |
| --- | --- | --- |
| `option` (etiket/değer) | `Select`, `AutoComplete`, `Dropdown`, `Menu`, `Pagination`, `Radio.Group`, `Tabs` | `options` (Select/AutoComplete) ya da `items` |
| `tabular` (satır) | `Grid` | `items` |
`option` bileşenlerinde binding'e `columns` eklenirse, ekranda görünmeyen kolonlar script'ten
`refs.<ad>.getColumn('kolon')` ile okunabilir. Bu, "müşteri seçilince vergi numarası da gelsin"
isteğinin tasarımcı tarafındaki karşılığıdır — ikinci bir istek atılmaz.
### 10.4 Veri kaynakları ve filtreler
Doküman seviyesindeki `dataSources` listesi, düğümlerin `bindings.sourceId` ile bağlandığı
uçlardır. Şeması `lowcode` §7.2'de; burada **filtre sözleşmesi** açılır.
| `source` | `value` ne demek | Ne zaman |
| --- | --- | --- |
| `static` | Değerin kendisi | Sabit kırılım (`IsActive = true`) |
| `query` | Sayfa adresindeki query parametresinin **adı** (`?id=…`) | Adresle taşınan bağlam |
| `route` | Yol segmentinin adı; boşsa **son segment** | `/admin/order/123` gibi rotalar |
| `record` | `<formRef>.<kolon>` — başka bir `Form` düğümünün aktif kaydının kolonu | Ana-detay; tek satır kod yazmadan |
| `operator` | Query string'e nasıl çıkar |
| --- | --- |
| `eq` | Çıplak parametre: `?RoleId=5` |
| `ne` `contains` `startswith` `endswith` `gt` `gte` `lt` `lte` `in` `isnull` `notnull` | Adını son ek olarak taşır: `?Name.contains=abc` |
`isnull` / `notnull` değer almaz (`DESIGNER_VALUELESS_FILTER_OPERATORS`); değer kutuları gizlenir.
`required` **varsayılan olarak `true`'dur ve bilinçlidir**: değeri henüz gelmemiş bir filtre
isteği tutar. Aksi hâlde filtresiz koleksiyon yüklenir ve bu, kullanıcıya "filtre çalışmıyor"
diye görünür. `previewValue` yalnızca tasarımcı kanvasında kullanılır, üretilen koda girmez.
Bu sözleşme CRUD Endpoint `GetList` ucunun okuduğu sözleşmenin aynısıdır (`lowcode` §9.1) —
yani tasarımcıdan kurulan filtre, sunucu tarafında ek bir şey yazmadan çalışır.
### 10.5 Property Panel — nasıl çalışır
Kanvasta seçili düğümün sağ paneli üç sekmedir ve her prop bir **kategoriye** aittir:
| Kategori | Nerede görünür | Ne taşır |
| --- | --- | --- |
| `properties` | Properties sekmesi | Davranış ve içerik prop'ları |
| `styling` | Properties sekmesi (stil grubu) | `className`, boyut, boşluk, hizalama |
| `events` | **JavaScript sekmesi** | `function` tipli prop'lar — değer değil **script** olarak düzenlenir (§7.8 · `lowcode`) |
Panelin kuralları:
- **Değer kümesi bilinen prop yazılmaz, seçilir.** `options` taşıyan (ya da tsType'ı bilinen bir
union olan) her string prop panelde açılır listeye döner (`withPickableOptions`); listede
olmayan mevcut değer korunur.
- **Olay listesi süzülür:** `DESIGNER_PRIMARY_EVENTS`'te kaydı olan bileşende yalnızca o olaylar
JavaScript sekmesinde görünür (aşağıda §10.10); kaydı olmayan bileşende bütün `function`
prop'ları listelenir.
- `field` ve `form` prop'ları panelde **hiç görünmez** (Formik'in runtime nesneleridir).
- Her düğümün panel-üstü iki ortak alanı vardır: **`ref`** (script adresi — `refs.<ad>`) ve
**`designerPermission`** (boş değilse düğüm yalnızca o yetkiyle çizilir).
- Seed dosyasına yalnızca **varsayılandan farklı** prop'ları yaz; panel dokunulmayan prop'u
dokümana koymaz.
### 10.6 `layout` bileşenleri — prop sözleşmesi
**`PageContainer`** — sayfanın dış kabı; çocuk kabul eder:
| Prop | Varsayılan | Anlam |
| --- | --- | --- |
| `maxWidth` | `"1280px"` | İçerik genişliği (CSS değeri) |
| `padding` | `24` | İç boşluk (px) |
| `gap` | `16` | Çocuklar arası dikey boşluk (px) |
| `className` | `""` | |
**`FlexRow`** — yatay bölme; çocuk kabul eder:
| Prop | Varsayılan | Anlam |
| --- | --- | --- |
| `columns` | `1` | `1` = serbest akan flex satırı; `2+` = o kadar **eşit grid kolonu** |
| `firstColumnWidth` | `""` | Yalnız kolon modunda: ilk kolonu sabitler → kenar çubuğu düzeni (`"320px"`) |
| `gap` | `16` | Kolonlar arası boşluk |
| `wrap` | `true` | Taşan çocuk alta insin mi (yalnız flex modunda) |
| `align` | `"stretch"` | `stretch` \| `start` \| `center` \| `end` |
| `className` | `""` | |
**`Table`** (`ui` kitinden, `layout` grubunda; çocuk kabul eder) — statik tablo:
`borderlessRow` (—), `compact` (`false`), `hoverable` (`true`), `overflow` (`true`),
`rowCount` (`2`), `columnCount` (`3`), `className`. `columns`/`data` prop'ları panelden
kaldırılmıştır — hücreler kanvasta çocuk düğüm olarak doldurulur. Veri bağlanabilir tablo
istiyorsan `Grid` (data) ya da `DataGridView` (platform) kullan.
**`Spacer`** (`ui` grubunda listelenir) — dikey boşluk: `height` (`24`).
### 10.7 `platform` bileşenleri — prop sözleşmesi
Dokuz görünüm düğümü de aynı üç prop'u alır; başka prop'ları **yoktur** (görünümün tüm davranışı gömülen
ListForm ekranının kendi tanımından gelir):
| Prop | Varsayılan | Anlam |
| --- | --- | --- |
| `listFormCode` | `""` | **Zorunlu.** Gömülecek ekranın kodu |
| `height` | `"520px"` (`ChartView`: `"420px"`) | Kapsayıcı yüksekliği |
| `className` | `""` | |
`ListView` · `DataGridView` · `TreeView` · `GanttView` · `TodoBoard` · `CardView` ·
`SchedulerView` · `PivotView` · `ChartView`. Bileşen, kullanıcının o ekranda yetkisi yoksa
ekranın kendi yetki hatasını gösterir; ayrıca gizlemek istiyorsan `designerPermission` yaz.
Gömülen görünüm, hedef ekranın `LayoutJson`'ında açık olmalıdır.
**Public site düğümleri** yalnızca public bileşenlerde (`PublicHeader`, `PublicFooter`, public
sayfalar) anlamlıdır; veriyi uygulama konfigürasyonundan (`publicMenus`, tema, dil) okur:
| Düğüm | Prop'lar (varsayılan) |
| --- | --- |
| `PublicHeaderBar` (çocuk kabul eder) | `scrolledClassName`, `scrollThreshold` (`10`) — sayfa kaydırılınca ek sınıf |
| `PublicNav` | `source` (`navigation` gezinme bağlantıları \| `actions` vurgulu/CTA kayıtlar \| `all`; kaynak `Pub` menüsündeki `App.Public.Header` düğümleri), `variant` (`horizontal` \| `vertical`), `showIcons` (`true`), `iconSize`, `itemClassName`, `activeItemClassName` |
| `PublicMobileMenu` (çocuk kabul eder) | `buttonClassName`, `panelClassName` |
| `PublicLogo` · `PublicThemeToggle` · `PublicLanguageSelector` · `PublicDemoButton` | Yalnız sınıf prop'ları (`imgClassName`, `buttonClassName`, `activeClassName`, `iconSize` `15`) |
Runtime kapsamında `react-icons`/`react-router` yoktur: ikon `PlatformIcon` (`name` `FaUser`,
`size` `24`), gezinme `a` düğümüdür. Kanvasın doğrudan çizdiği düğümler
`componentEditor/runtimeNodes.ts` sözlüğündedir; yeni düğüm = bileşen + bu sözlük + `catalog.ts` +
dil anahtarı.
### 10.8 `html` elemanları — prop sözleşmesi
| Eleman | Prop'lar | Olaylar |
| --- | --- | --- |
| `a` (çocuk kabul eder) | `children`, `href`, `target`, `aria-label`, `className`, `style` — public sayfalarda gezinmenin tek yolu | `onClick` |
| `div` (çocuk kabul eder) | `children`, `className`, `style` | `onClick`, `onMouseEnter`, `onMouseLeave` |
| `p` · `span` | `children`, `className` | `onClick` |
| `h1`–`h5` | `children`, `className` — başlıklar hazır Tailwind sınıfıyla düşer (`h1`: `text-4xl font-bold` … `h5`: `text-lg font-semibold`); Tailwind reset'i yüzünden sınıfı silersen başlık paragraf boyutuna iner | `onClick` |
| `img` | `src` (yeni düşen eleman gömülü SVG yer tutucusuyla gelir; herhangi bir URL/data-URI yazılabilir), `alt`, `width`, `height`, `loading`, `decoding`, `className` (`max-w-full rounded-md`) | `onClick`, `onLoad` |
`input`/`button`/`textarea`/`select`/`checkbox` etiketleri kutuda yoktur — `ui` karşılıklarını
kullan (§10.1).
### 10.9 `data` ve `ui` bileşenleri — Property Panel sözleşmeleri
Kaynak: `generated/componentProps.json` + `catalog.ts` düzeltmeleri. Her tabloda yalnızca
karar gerektiren prop'lar listelenir; `className`, `children`, `style` her bileşende vardır ve
tekrarlanmaz. `(z)` = zorunlu.
**Veri bileşenleri** (koleksiyon prop'u §10.3'e göre statik değer ya da data source binding'i
alır):
| Bileşen | Koleksiyon | Diğer prop'lar (varsayılan) |
| --- | --- | --- |
| `Select` | `options[]` (`{label, value}`) | `size` (`sm/md/lg`), `componentAs` (`''` \| `ReactSelect` \| `CreatableSelect` \| `AsyncSelect`), `value`, `isDisabled` (`false`), `isMulti` (`false`), `isClearable` (`false`), `isSearchable` (`true`) — tasarımcı `disabled/multiple/clearable/searchable` adlarını react-select karşılıklarına çevirir |
| `AutoComplete` | `options[]` | `value`, `defaultValue`, `placeholder`, `disabled` (`false`), `invalid` (`false`), `size`, `debounce`, `minChars` (`1`), `clearable` (`true`), `noOptionsText`, `loadingText`, `name` |
| `Dropdown` | `items[]` (`{eventKey, children, to}`) | `title` (`'Dropdown'`), `trigger` (`click` \| `hover` \| `context`), `placement` (`bottom-start`; 10 değerli liste), `disabled`, `activeKey`, `menuClass`, `menuStyle` |
| `Menu` | `items[]` (`{eventKey, icon, children/label}`) | `menuItemHeight` (`30`), `sideCollapsed` (`false`), `variant` (`light` \| `dark` \| `themed` \| `transparent`), `defaultActiveKeys[]`, `defaultExpandedKeys[]` |
| `Pagination` | `items[]` | `currentPage` (`1`), `pageSize` (`1`), `total` (`5`), `displayTotal` (`false`) |
| `Radio.Group` | `items[]` (`{value, label, disabled}`) | `name` (`'radioGroup'`), `value`, `vertical` (`false`), `disabled`, `readOnly`, `color` |
| `Tabs` | `items[]` (`{label, value}`) — **her sekme değeri bir çocuk yuvasıdır** (`slot: "tab:<value>"`), içine bileşen bırakılır | `defaultValue`, `value`, `variant` (`underline` \| `pill`) |
| `Grid` | `items[]` (satır nesneleri; kolonlar binding'den seçilir) | `cols` (`3`), `rows` (`2`), `gap`/`colGap`/`rowGap` (`4`), `responsive` (`true`), `smCols`/`mdCols`/`lgCols`/`xlCols` (`1/2/3/4`), `autoFit` (`false`), `minColWidth` (`'200px'`), `height` (`'auto'`) |
**Form/giriş bileşenleri:**
| Bileşen | Prop'lar (varsayılan) |
| --- | --- |
| `Input` | `type` (`text`; `HTMLInputTypeAttribute` listesi: `text/password/email/number/tel/url/search/date/time/…`), `value`, `placeholder` (`'Metin girin...'`), `textArea` (`false`) + `rows` (`3`), `prefix`/`suffix`, `size`, `disabled`, `invalid`, `readOnly`, `required`, `maxLength`/`minLength`, `pattern`, `autoComplete` (`off`), `autoFocus`, `name`, `unstyle` |
| `Checkbox` | `checked`, `defaultChecked`, `value`, `name`, `color`, `disabled`, `readOnly` |
| `Switcher` | `checked`, `defaultChecked`, `checkedContent`/`unCheckedContent`, `color`, `disabled`, `readOnly`, `isLoading` (`false`), `name` |
| `Radio` | `checked`, `defaultChecked`, `value`, `name`, `color`, `vertical`, `disabled`, `readOnly` — tekil düğme; seçenek grubu için `Radio.Group` |
| `DatePicker` | `value`/`defaultValue` (ISO string olarak saklanır, render'da `Date`'e çevrilir), `inputFormat`, `closePickerOnChange` (`true`), `defaultOpen` (`false`), `openPickerOnClear` (`false`), `inputtableBlurClose` |
| `TimeInput` | `value`/`defaultValue`, `format` (`24` \| `12`), `showSeconds` (`false`), `clearable` (`true`), `amLabel`/`pmLabel`, `timeFieldPlaceholder` (`'--'`), `prefix`/`suffix`, `size` (`md`), `disabled`, `invalid`, `name` |
| `Calendar` · `RangeCalendar` | `value` (`Date` \| `Date[]`), `multipleSelection` · `value` (`[Date, Date]`), `singleDate` (`false`) |
| `ColorPicker` | `value`/`defaultValue` (`'#6366f1'`), `presets[]`, `showAlpha`, `showInput` (`true`), `showRgb` (`false`), `size` (`md`), `disabled`, `name` |
| `Chips` | `value`/`defaultValue` (string dizisi; örnek `['Deneme','Deneme2']`), `max` (`0` = sınırsız), `allowDuplicate` (`false`), `separator`, `placeholder`, `size`, `disabled`, `invalid`, `name` |
| `Slider` | `value`/`defaultValue`, `min` (`0`), `max` (`100`), `step` (`1`), `range` (`false`), `orientation` (`horizontal` \| `vertical`), `marks`, `tooltip` (`false`), `trackSize` (`4`), `handleSize` (`18`), `disabled`, `readOnly`, `name` |
| `Knob` | `value`/`defaultValue` (`0`), `min`/`max` (`0/100`), `step` (`1`), `size` (`100`), `strokeWidth` (`14`), `valueColor`/`rangeColor`/`textColor`, `valueTemplate` (`'{value}'`), `showValue` (`true`), `readOnly`, `disabled`, `name` |
| `Rate` | `count` (`5`), `value`/`defaultValue`, `allowHalf` (`false`), `allowClear` (`true`), `color` (`amber`), `size` (`20`), `gap` (`4`), `tooltips[]`, `disabled`, `readOnly` |
| `Upload` | `accept`, `multiple`, `draggable` (`false`), `uploadLimit`, `showList` (`true`), `tip`, `fileList[]`, `disabled` — dosyanın **gerçek** yüklenmesi `beforeUpload`/`onChange` script'inde `api.post` ile yapılır |
| `BarcodeScanField` | `label`, `name` (`'barcode'`), `placeholder`, `buttonText`, `continuous` (`false` — açıkken diyalog okumadan sonra kapanmaz), `readOnly` (`true`), `className`; **olay**: `onScan(event)` — `event` okunan kodun metin hâli (Event workspace'te Input'un `onChange`'i gibi düzenlenir). Kamerayı `BarcodeDetector` (Android/Chromium) ya da lazy `zxing-wasm` (iOS/Firefox) ile okur — 1D + QR. **HTTPS şart**; iOS'ta yüklü PWA içinde yalnız 16.4+ çalışır, bileşen aksini kullanıcıya söyler |
| `BarcodeViewer` | `value` (barkoda çevrilecek veri), `format` (`QRCode` \| `Code128` \| `Code39` \| `Code93` \| `Codabar` \| `EAN13` \| `EAN8` \| `UPCA` \| `UPCE` \| `ITF` \| `DataMatrix` \| `PDF417` \| `Aztec`), `label`, `showText` (`false` — kodun altına okunabilir metni de basar), `scale` (`4`, modül kalınlığı), `fitWidth` (`false` — açıkken kapsayıcının genişliğine yayılır), `className`. Veriyi **SVG** olarak çizer (lazy `zxing-wasm/writer`); karanlık temada da okunabilsin diye beyaz zemin üstünde. `Form` düğümünün içinde `value` bir kolona bağlanırsa kayıt değiştikçe kod yeniden çizilir — tıpkı bir `Input` gibi. Veri formata uymuyorsa (EAN13'e harf gibi) kod yerine zxing'in hata metni görünür |
**Gösterim/geri bildirim bileşenleri:**
| Bileşen | Prop'lar (varsayılan) |
| --- | --- |
| `Button` | `variant` (`default` \| `solid` \| `twoTone` \| `plain`), `color`, `size` (`sm`), `shape` (`round` \| `circle` \| `none`), `icon`, `block` (`false`), `active` (`false`), `loading` (`false`), `disabled` |
| `Card` (çocuk kabul eder) | `header`/`headerExtra`/`footer`, `headerBorder`/`footerBorder` (`true`), `bordered`, `clickable` (`false`), `bodyClass`/`headerClass`/`footerClass` |
| `Avatar` | `src`/`srcSet`, `icon`, `alt`, `size` (`md`), `shape` (`circle`; `round` \| `circle` \| `square`) |
| `Badge` | `content`, `maxCount` (`99`), `innerClass`, `badgeStyle` |
| `Tag` | `children`, `prefix`/`suffix` (+ `prefixClass`/`suffixClass`), `title` |
| `Progress` | `variant` (`line` \| `circle`), `percent` (`0`), `color`, `showInfo` (`true`), `customInfo`, `strokeWidth` (`6`), `strokeLinecap` (`round` \| `square`), `width` (`120`; circle), `gapDegree`/`gapPosition` (circle) |
| `Skeleton` | `variant` (`block` \| `circle`), `animation` (`true`), `width`/`height`, `asElement` |
| `Spinner` | `size` (`20`), `color`, `isSpining` (`true`), `enableTheme` (`true`), `indicator` |
| `Notification` | `title`, `type` (`success` \| `warning` \| `danger` \| `info`), `closable` (`false`), `duration` (`3000`), `customIcon`, `width` (`350`) — sayfada sabit kutu olarak durur; olaya bağlı geçici bildirim için script'te `notify()` kullan |
| `Tooltip` | `title`, `placement` (`top`; 12 değerli `ArrowPlacement` listesi), `isOpen`, `wrapperClass` |
| `ImageViewer` | `images[]` (`{src, alt, caption}` — yeni düşen bileşen iki örnek görselle gelir), `children` (`'Galeri'` tetikleyici), `defaultIndex` (`0`), `loop` (`true`), `showToolbar`/`showThumbnails` (`true`), `zoomStep` (`0.25`), `minZoom`/`maxZoom` (`0.5/4`), `keyboard` |
| `Marquee` | `speed` (`100`), `direction` (`left` \| `right` \| `up` \| `down`), `pauseOnHover`/`pauseOnClick` (`false`), `loop` (`0` = sonsuz), `gradient` (`false`), `gradientColor`/`gradientWidth`, `delay` (`0`), `play` (`true`), `autoFill` (`false`) |
| `ScrollBar` | `direction` (`ltr` \| `rtl`) |
### 10.10 Olaylar — bileşen başına `event` yükü
JavaScript sekmesinde görünen olaylar ve handler'a gelen tek argümanın (`event`) içeriği.
Script kapsamındaki her şey (`refs`, `api`, `notify`, `translate`, `checkPermission`) ve
`Form` yaşam döngüsü olayları `lowcode` §7.8'dedir; buradaki tablo bileşen olaylarının yüküdür.
| Bileşen | Olay(lar) | `event` |
| --- | --- | --- |
| `Button` | `onClick` | DOM tıklama olayı |
| `Input` | `onChange` | DOM olayı → değer `event.target.value` |
| `Checkbox` | `onChange` | `event.checked` |
| `Select` | `onChange` | Seçilen `{label, value}` nesnesi; temizlenince `null` → `event?.value ?? null` |
| `AutoComplete` | `onInputChange` · `onSelect` | Yazılan metin · seçilen option nesnesi |
| `Dropdown` | `onSelect` | Seçilen öğenin `eventKey` değeri |
| `Menu` | `onSelect` | Seçilen öğenin `eventKey` değeri |
| `Pagination` | `onChange` | Seçilen sayfa numarası |
| `Radio.Group` | `onChange` | Seçilen öğenin `value` değeri |
| `Tabs` | `onChange` | Aktif sekmenin `value` değeri |
| `BarcodeScanField` | `onScan` | Okunan kodun metni |
Bu on bir bileşen `DESIGNER_PRIMARY_EVENTS` kaydıdır — panel yalnızca bu olayları gösterir.
Kayıtta olmayan bileşenlerde bütün `function` prop'ları listelenir; işe yarayanlar:
| Bileşen | Olaylar |
| --- | --- |
| `Switcher` · `ColorPicker` · `Knob` · `Slider` (+`onAfterChange`) · `Rate` (+`onHoverChange`) · `TimeInput` · `DatePicker` · `RangeCalendar` | `onChange` — yeni değer |
| `Chips` | `onChange`, `onAdd`, `onRemove` |
| `Upload` | `beforeUpload` (false → dosya reddedilir), `onChange` (dosya listesi), `onFileRemove` |
| `Card` · `Avatar` · `p` · `span` · `h1`–`h5` | `onClick` |
| `div` | `onClick`, `onMouseEnter`, `onMouseLeave` |
| `a` | `onClick` |
| `img` | `onClick`, `onLoad` |
| `Notification` · `ImageViewer` | `onClose` (`ImageViewer` ayrıca `onIndexChange`) |
| `Marquee` | `onFinish`, `onCycleComplete` |
Sayfa düzeyinde tek yaşam döngüsü kancası vardır: **`lifecycle.onMount`** — sayfa açıldığında
bir kez çalışır; açılış verisi çekme ve `refs` üzerinden ilk durumu kurma buraya yazılır.
> Olay davranışı kurarken önce Script Builder tarifleri (`lowcode` §7.8 tablosu) denenir;
> serbest kod `custom` tarifiyle eklenir ki sihirbaz açık kalsın.
---
## 11. Zamanlanmış e-posta (MailQueue modülü)
`BackgroundWorker` kaydı (`Sas_H_BackgroundWorker`, ekran Saas › Background Workers ›
Recurring Jobs) `WorkerType: 1` (MailQueueWorker) ise gövde bu modüldür
(`api/modules/Sozsoft.MailQueue`). Diğer tipler: `2` SqlWorker (`BeforeSp`/`AfterSp`) · `3`
NotificationWorker · `4` SessionCleanupWorker · `5` BackupWorker · `6` ProbationReminderWorker. `Scope`: `1` Host · `2` Tenant ·
`3` All (host + her kiracı) — kiracı tablosuna dokunan iş `Host` olamaz (`README.md` §14). Hazır
işler `data/App.BackgroundWorkers.RecurringJobs.json` ile gelir.
### 11.1 Worker `Options` (`MailQueueWorkerOptions`)
| Anahtar | Anlam |
| --- | --- |
| `MailType` | Şablon tipi etiketi |
| `MailSubject` | Mail konusu |
| `MailTemplate` | Razor ile render edilen HTML gövde (`EmailLayout` düzeni içinde) |
| `TableName` | Maildeki tabloların tarifi (§11.2) |
### 11.2 `TableName` biçimi
Her tablo `{index:TabloAdi:DosyaTipi:MailEkAdi:Korumalimi:DosyaAciklama}` altılısıdır;
birden fazlası `};{` ile birleştirilir:
```
{0:MT3_GECIKEN_SIPARIS:IN:Order Delay Notification:0:};{1:MT7_SIPARIS_DURUM:PDF:Durum.pdf:0:BRU,3,1}
```
- `TabloAdi` bir **tablo/view adıdır**; `SELECT * FROM {TabloAdi} WHERE ...` olarak parametreli
çalıştırılır (parametreler kuyruk satırındaki `TableParameter` alanından gelir).
- `DosyaTipi` = `IN` ise tablo mail gövdesine gömülür; `PDF` / `XLS` / `TXT` ise ek dosya olur.
- Sütun başlıkları, sırası, CSS'i ve alt toplamları `Sas_H_BackgroundWorker_MailQueueTableFormat`
kayıtlarından okunur: `TableName`, `Order`, `ColumnName`, `Caption`, `HeaderCss`, `Css`,
`FooterCss`, `DataType`, `DataFormat`, `IsHidden`, `IsProtected`, `SubTotal`, `Width`.
### 11.3 Kuyruk satırı (`Sas_H_BackgroundWorker_MailQueue`)
`TemplateId` (worker), `From`, `To`, `MailParameter`, `TableName`, `TableParameter`,
`Attachment`, `AttachmentParameter`, `RelatedRecordId`, `SendStatus`, `SendTime`, `AwsMessageId`.
Gönderim sonuçları `Sas_H_BackgroundWorker_MailQueueEvents` tablosunda izlenir. Tabloların
ekranı yoktur; satırı kanca/prosedür yazar.
Kuyruğa satır yazma işi genellikle worker'ın `BeforeSp` prosedüründe ya da ekranın
`InsertAfterCommand` kancasında yapılır — yani "kayıt onaylanınca mail çıksın" isteği kod
gerektirmez.
---
## 12. Saas, Administration ve İK menüleri — platform yetenek kataloğu
Bu üç menü, platformun **hazır gelen tüm yeteneklerinin** girişidir. Bir komponent üretirken
buradaki her ekran üç rolden birindedir:
- **Yeniden kullanılacak yetenek** — numaratör, bildirim, zamanlanmış iş, rapor, dosya, yetki,
dil, ayar. Talebin bu kısmı için yeni artefakt üretilmez; ilgili ekrana kayıt girilir.
- **Veri kaynağı** — ülke/il/ilçe, para birimi, ölçü birimi, departman, ünvan, şube, kullanıcı,
rol. Yeni ekranın lookup'ı bu tablolara bağlanır; aynı listeyi tekrar tanımlama.
- **Referans uygulama** — istenen davranışın bu uygulamada zaten çalışan bir örneği. Aynı JSON'u
oradan alıp uyarlamak, sıfırdan yazmaktan hem hızlı hem güvenlidir (§12.7).
### 12.1 Tablo adlandırma sözleşmesi
Platform tablolarının adı `TableNameResolver` tarafından üretilir:
```
{ModülÖnEki}_{H|T|B}_{TableNameEnum} view'ler: V_{aynı ad}
```
| Parça | Değerler |
| --- | --- |
| Modül ön eki (`MenuPrefix`) | Yalnızca `Sas` (Saas) · `Adm` (Administration) · `Hr` (İK — `ListFormSeeder_Hr`, menü/yetki grubu `App.Hr`, modül `Hr`). Modüller (`api/modules/*`) kendi `*DbProperties.MenuPrefix` değerini taşır (MailQueue/Settings/Languages `Sas`, Notifications `Adm`) |
| Kapsam | `H` host (tüm tenant'lar için ortak) · `T` tenant · `B` şube (`TablePrefix.BranchByName`; şu an eşlemesi olan tablo yok) |
| Ad | `TableNameEnum` üyesi |
Örnek: `Sas_H_Currency` (host — tüm kiracılar aynı para birimlerini görür), `Hr_T_Department`
(kiracı başına), `V_Sas_H_Order` (view). Menü ile tablo öneki her zaman örtüşmez:
`Sas_T_GlobalSearch`, `Sas_T_Branch`, `Sas_T_Forum*`, `Sas_T_IntranetWidget` kiracı tablosudur. Özel view'ler
`TableNameResolver.GetFullSpecialViewName` ile `V_{Önek}_{Prefix}_{Ad}` biçiminde üretilir.
> Yeni bir platform tablosu eklenecekse `TableNameEnum` + `TableNameResolver._map` birlikte
> güncellenir; eşlemesi olmayan ad `KeyNotFoundException` fırlatır. Proje tabloları için
> `lowcode.instructions.md` §2'deki `{Modul}_T_{Entity}` kuralı geçerlidir.
### 12.2 Saas menüsü
Kök menü `App.Saas`. Yetki adı her satırda menünün `RequiredPermissionName` değeridir; ListForm
ekranlarında alt yetkiler `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note`
soneklerini alır.
| Ekran | Kod / route | Tablo | Kapsam | Komponent üretirken ne işe yarar |
| --- | --- | --- | --- | --- |
| **Definitions › AiBot** | `App.Definitions.AiBot` | `Sas_H_AiBot` | host | Yapay zekâ asistanı tanımı (`Name`, `ApiUrl`, `Tenants`); `/admin/ai` bu kayıtları kullanır. |
| **Definitions › Global Search** | `App.Definitions.GlobalSearch` | `Sas_T_GlobalSearch` | tenant | Üretilen ekranın üst aramada çıkması için kayıt: `System`, `Group`, `Term`, `Weight`, `Url`. |
| **Definitions › Contact Title** | `App.Definitions.ContactTitle` | `Sas_H_ContactTitle` | host | Ünvan/hitap lookup'ı (`Title`, `Abbreviation`). |
| **Definitions › Termination Reasons** | `App.Definitions.TerminationReason` | `Sas_H_TerminationReason` | host | SGK işten çıkış kodları (`Code`, `Name`, `IsActive`); mevzuat kodu olduğu için bütün kiracılarda ortaktır. Seed `data/App.Definitions.TerminationReason.json` (06/07 mülga). Kullanıcının `TerminationReasonId` alanı `LookupQueryValues.TerminationReasonValues` ile buraya bağlanır. |
| **Definitions › Application Suites** | `App.Definitions.ApplicationSuite` | `Sas_H_ApplicationSuite` | host | Kiracıya açılan modül paketi (`Name` benzersiz, `Modules` `Sas_H_Module`'den çoklu `dxTagBox` — `\|` ile, `IsActive`). Sales Orders'ın kiracı oluşturma diyaloğu aktif paketleri `GET list-form-dynamic-api/application-suites` (`Tenants.Create`) ile okur ve seçilenin modüllerini forma yazar. Seed `data/App.Definitions.ApplicationSuite.json`. |
| **Definitions › Currency** | `App.Definitions.Currency` | `Sas_H_Currency` | host | Para birimi + `Rate`. Tutar alanı olan ekranın kur lookup'ı ve `DbQuery` varsayılanı buradan beslenir. |
| **Definitions › Country Group / Country / City / District** | `App.Definitions.CountryGroup` · `.Country` · `.City` · `.District` | `Sas_H_CountryGroup` · `Sas_H_Country` · `Sas_H_City` · `Sas_H_District` | host | Adres alanlarının hazır cascade zinciri (dört ayrı menü ve seed dosyası; `Order` sırası grup → ülke → il → ilçe). `Country` ayrıca `PhoneCode`, `PhoneNumberFormat`, `ZipRequired`, `StateRequired`, `TaxLabel` taşır — adres doğrulaması için kullan. |
| **Public › Products / Payment Methods / Installment Options** | `App.Orders.Products` · `.PaymentMethods` · `.InstallmentOptions` | `Sas_H_Product` · `Sas_H_PaymentMethod` · `Sas_H_InstallmentOption` | host | Ürün, ödeme yöntemi (`Commission`), taksit — satış/abonelik ekranlarının ve public `/products` sayfasının kaynağı. |
| **Public › Sales Orders** | `App.Orders.SalesOrders` (alt form Sales Order Item) | `Sas_H_Order` + `Sas_H_OrderItem` | host | **Onay akışı + alt kalem + toplam + diyalog** birlikte kullanılan tam örnek (§12.7). Onaylanan siparişin `Tenant` komutu `CreateTenantFromOrderDialog`'u açar; diyalogda seçilen Application Suite modülleri doldurur (değiştirilebilir). |
| **Public › Blog Category / Posts** | `App.BlogManagement.Category` · `.Posts` | `Sas_H_BlogCategory` · `Sas_H_BlogPost` | host | Çok dilli içerik (`ContentTr`/`ContentEn`), slug, yayın durumu, sayaç alanları; public `/blog` bu tablolardan okur, yayınlanmış yazılar sitemap'e girer. |
| **Public › Demos** | `App.Demos` | `Sas_H_Demo` | host | Public `/demo` formundan gelen talepler — public form → tablo akışının örneği. |
| **Tenants** | `AbpTenantManagement.Tenants` | `AbpTenants` | host | Kiracı kaydı, bağlantı dizesi diyaloğu (`TenantsConnectionString`), alt form olarak şubeler; yazma `list-form-dynamic-api/tenant-*`. Ekranların `IsTenant` davranışının kaynağı. |
| **Branches** | `App.Branches` | `Sas_T_Branch` | tenant | Şube listesi. Ekranı `IsBranch = true` yaparsan filtre bu tablodan gelir; `BranchSeed` diyaloğu şube verisini kurar; `App.Branches.Update` ister ve kiracı yalnızca kendi şubesini kurabilir. |
| **Setting Definitions** | `App.SettingDefinitions` | `Sas_H_SettingDefinition` | host | Yeni uygulama ayarı: `Code`, `MainGroupKey`/`SubGroupKey`, `DataType`, `SelectOptions` (`List` tipinde değer → etiket; etiket dil anahtarıdır, Ayarlar ekranı `::` önekiyle çevirir, anahtar yoksa metin olduğu gibi görünür), `DefaultValue`, `Providers`, `RequiredPermissionName`, `IsEncrypted`, `IsVisibleToClients`. Hazır tanımlar `data/App.SettingDefinitions.json`'dan gelir. Ayarlanabilir olması gereken değeri koda gömme. |
| **Languages › Language / Language Text** | `App.Languages.Language` · `.LanguageText` | `Sas_H_Language` · `Sas_H_LanguageKey` · `Sas_H_LanguageText` | host | Dil anahtarları. Üretilen her ekranın EN+TR metinleri buraya yazılır; ekran `data/App.Languages.{Language,LanguageText}.json` dosyalarına senkronlanır. Metin insert'i eksik `LanguageKey`'i de üretir, aynı kültür+kaynak+anahtar varsa eklemez. |
| **Data Source** | `App.DataSource` | `Sas_H_DataSource` | host | `Code`, `DataSourceType`, `ConnectionString`. Tipli API (`/api/app/data-source`) bağlantı cümlesini hiçbir zaman döndürmez; okuma açıktır (açılır listeler), yazma `App.DataSource.Create/Update/Delete` ister. **İki tür kayıt bir arada:** elle tanımlanan host/harici bağlantılar ve `Code = tenant adı` olan otomatik tenant bağlantıları (Tenants ekranındaki bağlantı diyaloğu yönetir, elle dokunma). Varsayılan kaydı `HostDataSeeder` appsettings'teki bağlantı cümlesinden kurar. Çözüm kuralı `lowcode` §9.3.3. Bağlantı dizesi koda/JSON'a yazılmaz. |
| **Background Workers › Recurring Jobs / Jobs** | `App.BackgroundWorkers.RecurringJobs` · `.Jobs` | `Sas_H_BackgroundWorker` · `Sas_H_BackgroundWorker_JobFlow` | host | Cron'lu işler ve çalıştırma geçmişi (`Tenant`, `ExecutionDate`, `Duration`, `Status`, `FailureReason`, `ExceptionDetails`). `WorkerType`/`Scope` değerleri §11'de; her kapsam ayrı bir Hangfire işidir, `Tenant` kolonu hangi kiracıda çalıştığını gösterir. Hazır işler (oturum temizliği, bildirim, yedek, şema sözlüğü) `data/App.BackgroundWorkers.RecurringJobs.json`'dan gelir. Ayrıntı: `lowcode` §9.10, `README.md` §14. |
| **Menus › Routes / Menu / Manager** · **Definitions › Module** | `App.Menus.Module` · `.Routes` · `.Menu` · `.Manager` (`/admin/menuManager`) | `Sas_H_Module` · `Sas_H_Route` · `Sas_H_Menu` | host | Menü ve route veritabanından gelir; üretilen ekranın menü kaydı buradan açılır. `Menu` ekranı ağaç düzeninin referansıdır. `Module` ekranının menü ve yetkisi Saas › Definitions altındadır (Applications ekranının hemen altı); ekran kodu `App.Menus.Module` olarak kalır. Seed: `data/App.Menus.{Routes,Module,Menu}.json` — `Routes` dosyasına yalnızca var olan satırın `Seo*` alanları için dokunulur. |
| **Developer Kit › Dev Requests** | `App.DevRequests` | `Sas_H_DevRequest` | host | **Kanban/Todo düzeninin** referansı (`DefaultLayout: todo`): `Status`, `Priority`, `SortOrder`, `Tags`, `Assignees`, `SubTasks`, `Timesheets`; detayda `Description` yanında `Analysis` sekmesi (`AnalysisExpr`). MCP geliştirme döngüsünün kuyruğudur (`Backlog` → `Analysis` MCP analiz eder, `Ready to Start`'a kullanıcı alır; `Ready to Start` → `Testing`, `Staging`'e kullanıcı alır; araç çubuğunda DB Migrate butonu vardır — `UiEvalService.ApiDbMigrate()`, önce onay sorar, `App.Setup.Migrate`; yalnızca `start_dev_request` başlatır; açıklamadaki base64 görseller MCP'ye resim içeriği olarak gider, analiz `save_dev_request_analysis` ile yazılır, üretim notu `append_dev_request_note` ile `Analysis`'e eklenir — `Description` talebin yazıldığı hâliyle kalır; `lowcode.instructions.md` §0.11–§0.12). |
| **Developer Kit › SQL Query Manager** | `App.SqlQueryManager` · `/admin/sqlQueryManager` | `Sas_H_CrudEndpoint` (üretilen CRUD uçları) | — | View/procedure/function/tablo tasarımı ve çalıştırma, tablodan CRUD endpoint üretimi (`.CrudEndpoints` yetkisi); karar sırasının 2. adımı buradan başlar. |
| **Developer Kit › Files** | `App.Files` · `/admin/files` | Blob depolama | tenant | Dosya yöneticisi. Ekranın dosya alanı için ayrı depolama kurma; dosya/görsel editörleri ve Intranet Documents bu altyapıyı kullanır. |
| **Developer Kit › Custom Endpoints** | `App.DeveloperKit.CustomEndpoints` | `Sas_H_CustomEndpoint` | host | `Name`, `Url`, `Method`, `DataSourceCode`, `Sql`, `ParametersJson` (`Static`/`Query`/`Path`/`Body`), `PermissionsJson` (`Global`/`Role`/`User`) — parametreli SQL'i yetkili bir HTTP ucuna çevirir. |
| **Developer Kit › Dynamic Services** | `App.DeveloperKit.DynamicServices` | `Sas_H_DynamicService` | host | Liste ListForm ekranıdır; kod, toolbar/satır butonlarından açılan `DynamicServiceEditor` diyalogunda (Monaco) yazılıp derlenir ve yayınlanır (`.TestCompile`, `.Publish`, `.ViewCode`). SQL'in yetmediği iş kuralı için 3. adım. |
| **Developer Kit › Components** | `App.DeveloperKit.CustomComponents` | `Sas_H_CustomComponent` | host | Custom component listesi ve satırdaki **Design** butonuyla **Visual Designer** girişi (§10; `/admin/developerkit/components/design/:id`). Kayıtlar `data/App.DeveloperKit.CustomComponents/{Id}.json` klasörüne satır başına senkronlanır; public sayfalar ile üst/alt bilgi de buradadır. |
| **Developer Kit › ListForm** | `App.Listforms.Listform` | `Sas_H_ListForm` · `Sas_H_ListFormField` · `Sas_H_ListFormWorkflow` · `Sas_H_ListFormCustomization` · `Sas_H_ListFormImport` | host | Ekran tanımlarının kendisi. Bu dosyada anlatılan bütün JSON kolonlarının canlı hâli; düzenleyicisi `/admin/listform/edit/{kod}`. |
| **Developer Kit › Intranet Widgets** | `App.DeveloperKit.IntranetWidgets` | `Sas_T_IntranetWidget` | tenant | Intranet panosunda hangi widget'ın hangi kolonda (`Column`), hangi sırada (`Order`) ve hangi yetkiyle (`PermissionName`) çizileceği; `SourceType` `Builtin` (koddaki `Code`) ya da `Custom` (`ComponentName` ile bir custom component). Seed: `data/App.DeveloperKit.IntranetWidgets.json`. |
| **Developer Kit › MCP Servers** | `App.DeveloperKit.McpServers` | `Sas_H_McpServer` | host | `/mcp` ucunun kiracı bazlı sözleşmesi: `Name`, `Url`, `Tenants`, `AllowedTools` (boşsa tüm araçlar, doluysa yalnızca seçilenler), `MaxRows`. Yetkiler: ekran `App.DeveloperKit.McpServers`, erişim ve okuma `.Invoke`, her değişiklik `.Write` (kiracıda satır yazma, host'ta ayrıca tablo/ekran/bileşen geliştirme) — arkasındaki ekran C/U/D, Wizard ve Custom Components yetkileri ayrıca istenir. Kiracı veri okur ve yazar, geliştirme yapamaz (`TenantTools`: şema araçları, `query_list`, `run_select`, `insert_row`/`update_row`/`delete_row`); ham SQL `App.SqlQueryManager` ister; MCP yetki vermez. Kayıtlar `data/App.DeveloperKit.McpServers.json`'a senkronlanır. Ayrıntı: `README.md` §13.9. |
| **Developer Kit › Wizard Manager** | menü kodu `App.Listforms.WizardManager` · yetki `App.Listforms.Wizard` · `/admin/listform/wizardManager` | `configs/seeds/wizard/*.json` | host | Wizard seed dosyalarının kart/liste görünümü: menü üst düğümü ve URL, **bağımlılıklar**, düzenleme, export (bağımlılık kapanışı) / import ve **silme** (`WizardRecordCleaner`: ekran, veri aynası ve izlenen kayıtları kaldırılır, başka ekran kullanıyorsa engellenir, sonuç raporlanır; onay diyaloğu ekranın tablosunu sorar, seçilirse `WizardTableCleaner` tabloyu scripti ve crud dosyasıyla düşürür — `lowcode` §4.5). Sihirbazın kendisi `/admin/listform/wizard`. |
| **Forum Management › Categories / Topics / Posts** | `App.ForumManagement.Categories` · `.Topics` · `.Posts` | `Sas_T_ForumCategory` · `Sas_T_ForumTopic` · `Sas_T_ForumPost` | tenant | Forumun yönetim tarafı: kategori (seed `data/App.ForumManagement.Categories.json`), konu (kategori lookup'ı, sayaçlar, sabitle/kilitle/çözüldü bayrakları), gönderi (konu lookup'ı, kabul edilen yanıt). Kullanıcı tarafı Administration'daki `/admin/forum`. |
> Menü kodu ile yetki adı **her zaman aynı değildir**: Wizard Manager'da kod
> `App.Listforms.WizardManager`, yetki `App.Listforms.Wizard`'dır; Administration'daki Forum
> ekranında kod `App.Forum`, yetki `App.ForumManagement.Publish`'tir. Org Chart'ta menü ve
> `/admin/organization` rotası aynı yetkiyi (`App.Hr.OrgChart`) ister. Yeni ürettiğin ekranlarda
> ikisini ayırma — wizard zaten aynı kökü kullanır (`lowcode` §2).
### 12.3 Administration menüsü
Kök menü `App.Administration`. Tabloların çoğu `Adm_T_*` — yani **kiracı başına** ayrışır; Identity
ekranları ABP tablolarıdır, Licence/Dev Request host tablolarını okur.
| Ekran | Kod / route | Tablo | Komponent üretirken ne işe yarar |
| --- | --- | --- | --- |
| **Definitions › Sector** | `App.Definitions.Sector` | `Adm_T_Sector` | Sektör lookup’ı. Veri `data/App.Definitions.Sector.json`'dan seed edilir. |
| **Definitions › Contacts** | `App.Definitions.Contact` (menüsüz sekmeler `.ContactSales`, `.ContactPurchasing`, `.ContactFinancial`, `.ContactAccounting`, `.ContactNotes`, `.ContactPerson`, `.ContactBankAccount`) | `Adm_T_Contact` · `Adm_T_ContactPerson` · `Adm_T_ContactBankAccount` | Satış ve satınalmanın ortak müşteri/tedarikçi kartı; belge ekranlarında cari lookup'ı buradan alınır (`IsActive = 1`, `TenantId = '@TENANTID'`). Wizard ekranıdır, ayrıntı `configs/seeds/docs/modules/App.Administration.md` › Tanımlamalar. Seed `data/App.Definitions.Contact.json`. |
| **Definitions › Contact Tags** | `App.Definitions.ContactTag` | `Adm_T_ContactTag` | Kontak sınıflandırma etiketleri; Kontaklar'ın `Tags` alanı (`dxTagBox`) buradan seçer. Seed `data/App.Definitions.ContactTag.json`. |
| **Definitions › Sequence** | `App.Definitions.Sequence` | `Adm_T_Sequence` | Belge numarası üretimi (`lowcode` §9.9); alan varsayılanında `CustomValueType: 5` ile bağlanır. Entity `IMultiTenant`'tır; ekran `IsTenant = true`, yetkiler `MultiTenancySide = Both` — her kiracı kendi numaratörlerini tutar. Seed `data/App.Definitions.Sequence.json`. |
| **Definitions › UoM Category** | `App.Definitions.UomCategory` (alt form `.Uom`) | `Adm_T_UomCategory` · `Adm_T_Uom` | Ölçü birimi, `Ratio`/`Rounding` — miktar alanı olan ekranlarda birim lookup'ı ve çevrim. Entity'ler `IMultiTenant`'tır; ekranlar `IsTenant = true`, yetkiler `MultiTenancySide = Both`. Seed `data/App.Definitions.{UomCategory,Uom}.json`. |
| **Licence** | `App.Licence` · `/admin/form/App.Licence` | `AbpTenants` + `V_Sas_H_OrderItem_ByTenantId` | **`ListFormType = Form`** (listesiz, tek kayıtlık ekran) + alt kalem listesi. "Tek kayıtlık kart ekran" isteğinin karşılığı budur. |
| **Dev Request** | `App.DevRequest` | `Sas_H_DevRequest` | Kiracı tarafındaki talep ekranı (Saas'taki listenin kullanıcı yüzü; `UserName` varsayılanı `@USERNAME`). |
| **Bildirimler › Bildirim Şablonları** | `App.Notifications.NotificationTemplates` | `Adm_T_NotificationTemplate` | Elle gönderimde seçilen mesaj metinleri: `Code`, `Name`, `Channel` (boş = tüm kanallar), `Subject`, `Body` (`{AdSoyad}`, `{KullaniciAdi}`, `{Eposta}`, `{Tarih}`, `{Saat}`, `{Kiraci}` yer tutucuları), `IsActive`. Boş bırakılan konu/gövde şablondan tamamlanır. Seed: `data/App.Notifications.NotificationTemplates.json`. |
| **Bildirimler › Bildirim Türleri / Kuralları** | `App.Notifications.NotificationTypes` · `.NotificationRules` | `Adm_T_NotificationType` · `Adm_T_NotificationRule` | Olay bazlı bildirim: tür + kanal + alıcı (`All`/`User`/`Role`/`OrganizationUnit`/`Custom`), `IsFixed` zorunlu kural. 8 kanal (`Sms`, `Mail`, `Rocket`, `Desktop`, `UiActivity`, `UiToast`, `WhatsApp`, `Telegram`); etiketleri `App.NotificationChannel.{Kanal}` dil anahtarlarıdır. Tür ve kural kiracıya aittir, yeni kiracıya varsayılan küme kopyalanır (`NotificationTenantDefaultsSeedContributor`). Kurallar ekranında `CreateNotification` diyaloğu da vardır. Ayrıntı: `lowcode` §9.11, `README.md` §13. |
| **Bildirimler › Bildirimler** | `App.Notifications.Notification` | `Adm_T_Notification` | Hem kuyruk günlüğü hem elle gönderim ekranı: liste kural motorunun ürettiklerini de gösterir, ekleme formu alıcı × kanal açılımını kuyruğa yazar (`InsertServiceAddress = list-form-dynamic-api/notification-insert`; `dxTagBox` çoklu alıcı/kanal). Tip her zaman `Manuel`, güncelleme kapalı. Ayrı bir manuel gönderim tablosu yoktur. Ayrıntı: `README.md` §13.5.1. |
| **Agenda** | `App.Agenda` | `Adm_T_Agenda` | **Takvim (Scheduler) düzeninin** referansı (`DefaultLayout: scheduler`): `StartDate`, `EndDate`, `AllDay`, `RecurrenceRule`, `RecurrenceException`. Tekrarlayan randevu isteği kod gerektirmez. |
| **Settings** | `App.Setting` · `/admin/settings` | — | `SettingDefinition` kayıtlarından üretilen ayar ekranı; değerler burada girilir. `List` seçeneklerinin etiketi dil anahtarıdır. |
| **Restrictions › Work Hour** | `App.Restrictions.WorkHour` | `Adm_T_WorkHour` | Gün bazlı çalışma saati (`Monday`…`Sunday`, `StartTime`, `EndTime`); kullanıcıya `AbpUsers.WorkHour` ile bağlanır. "Sadece mesai saatinde girilebilsin" burada çözülür. |
| **Restrictions › IP Restrictions** | `App.Restrictions.IpRestrictions` | `Adm_T_IpRestriction` | `ResourceType` + `ResourceId` + `IP` — kullanıcı/rol bazlı IP kısıtı. |
| **Identity › Permission Groups / Permissions** | `Abp.Identity.PermissionGroups` · `Abp.Identity.Permissions` | `AbpPermissionGroups` · `AbpPermissions` | Yetki ağacı (tree düzeni). Yeni ekranın yetkileri burada görünür; `Modules` kolonu yetkiyi modüllere bağlar (pipe ile çoklu), `Order` kardeşler arası sıradır. Seed: `data/Abp.Identity.Permission{Groups,s}.json`. |
| **Identity › Claim Type** | `App.IdentityManagement.ClaimType` | `AbpClaimTypes` | Kullanıcıya ek alan (claim) tanımı: `ValueType`, `Regex`, `Required`, `IsStatic`. |
| **Identity › Roles** | `AbpIdentity.Roles` | `AbpRoles` | Rol yönetimi + `RolesPermission` diyaloğu; yeni ekranın yetkisi role buradan verilir. Yazma `list-form-dynamic-api/role-*`. |
| **Identity › Users** | `AbpIdentity.Users` · detay `/admin/users/detail/:userId` | `AbpUsers` | Kullanıcı yönetimi + `UsersPermission` diyaloğu (detay ve kullanıcı bilgisi kaydı `AbpIdentity.Users.Update`, "Yetkili kullanıcılar" `App.Listforms.Listform.Update`, oturum sonlandırma `…IdentityManagement.Sessions` ister; istemciden gelen şube kimlikleri kiracının kendi şubelerine süzülür), **KPI widget'ları** ve `list-form-dynamic-api/user-*` ile devredilen yazma. Kullanıcı `WorkHour`, `DepartmentId`, `JobPositionId`, `Gender` (`Male`/`Female`, `App.StaticLookup.*`) ve `HomeAddress` alanlarını liste ve formda taşır; kişisel ve kimlik bilgileri (`IdentityNumber`, `BirthDate`, `EducationLevel`, `Nationality`, `SskNo`…) detay ekranındadır. Detay sekmeleri karışmasın diye gruplanmıştır: **General** (iletişim + ikinci e-posta/telefon, ek bilgi, avatar, askerlik `MilitaryStatus`/`MilitaryPostponeEndDate`, engellilik `IsDisabled`/`DisabilityRate`/`DisabilityReport` — rapor base64, 5 MB), **Identity** (nüfus cüzdanı, doğum ve aile, uyruk Türkiye değilse yabancı uyruk `Passport*`/`WorkPermit*`), **Work** (organizasyon `RegistrationNo`/`SskNo`/`ShiftId`/`PdksCardNo`, sözleşme `EmploymentType`/`ContractType`/`ContractEndDate`/`ProbationEndDate`/`SeniorityStartDate`, çıkış `TerminationDate`/`TerminationReasonId`/`TerminationNote`, sürücü belgesi `DrivingLicense*` — sınıflar `|` ile, iletişim kanalları; altında iş deneyimi), **Education & Skills** (en yüksek `EducationLevel` + eğitim/dil/yetenek/belge alt listeleri), **Family / Emergency**, **Financial** (`BankName`, `Iban`, `SalaryType`, `GrossSalary`, `SalaryCurrency` + yan haklar), Health, Permissions, Lockout, Claims. Deneme süresi işe girişten en fazla dört ay sonra biter (`UpdateUserAsync` ve ekran doğrular). Ücret alanları `UserInfoViewModel`'de taşınmaz — o model intranet doğum günü listesine de akar; `GET {id}/financial` / `PUT financial` uçlarından okunur, yetki detayınkiyle aynıdır (`AbpIdentity.Users.Update`). Aynı sebeple `DisabilityReport` yalnızca `GetByIdAsync`'te doldurulur. `ShiftId`, `TerminationReasonId`, `SalaryCurrency` seçenekleri ekranda `list-form-select/lookup` ile bu ListForm'un alan lookup'larından okunur (`useUserFieldLookup`). Detaydaki **Sağlık Kartı** sekmesi `BloodType` ile soy geçmişini (`{Mother,Father,Sibling,Child}HealthStatus` — `Alive`/`Deceased`, `App.StaticLookup.*` — ve `…HealthNote`) ve öz geçmişini (`ChronicDisease`, `TetanusVaccination`, `HepatitisVaccination`, `OtherVaccination`) taşır; sekme dört gruptur (Soy/Öz Geçmişi, Laboratuvar - Fizik Muayene `Lab*`/`Exam*`, Anamnez `Complaint*`/`Diagnosis*` + 3-10. sorular, Sonuç `FitForWork*`/`ShiftWorkFit`/`NightWorkFit`). Sonuçtaki hekim (`FitnessDoctorId`, `ShiftNightDoctorId`) wizard tablosu `Hr_T_Doctor`'dan `LookupQueryValues.TenantDoctorValues` ile seçilir; diploma ve işyeri hekimliği belgesi hekimden gösterilir, kullanıcıya yazılmaz. Bu alanların hepsi listede gizli alandır; arayüz `user-management/HealthCardTab.tsx`. Kullanıcı seçen her lookup bu bilgileri `AbpUsers`'tan okur ve kopya kolon aynı ad/tip/değer kümesiyle açılır (`lowcode.instructions.md` §0.12 madde 5). Yeni bir kullanıcı alanı = `PlatformConsts.AbpIdentity.User` + `AbpIdentityUserExtensions` + EF eşlemesi ve migration + `UserInfoViewModel` (detay/profil) + `CreateUpdateUserInput` ve `user-insert`/`user-update` (liste) + `ListFormSeeder_Administration` alanı + gerekiyorsa yönetici varsayılanı (`PlatformConsts.AbpIdentity.User.Admin*DefaultValue` + `PlatformIdentityDataSeeder`; host ve tenant Seed sekmesinin yöneticisi buradan oluşur). |
| **Identity › Users › Work Experience** | `App.IdentityManagement.UserWorkExperience` (menüsüz alt liste) | `Adm_T_UserWorkExperience` | Kullanıcının daha önce çalıştığı yerler: `CompanyName`, `SectorId` (Sektörler lookup'ı, `LookupQueryValues.TenantSectorValues`), `StartDate`, `EndDate`, `JobDescription` (HtmlEditor). Entity `IMultiTenant`; kullanıcı detayının Work sekmesinde `PlatformViewHost` ile `UserId` filtresiyle gömülüdür, yeni satır `UserId`'yi filtreden alır. Kendi yetkisi yoktur: okuma `AbpIdentity.Users`, ekleme/güncelleme/silme detay sayfasının yetkisi `AbpIdentity.Users.Update`. |
| **Identity › Users › Health Card lists** | `App.IdentityManagement.UserPeriodicExamination` · `.UserPatientProtocol` · `.UserSickLeave` (menüsüz alt listeler) | `Adm_T_UserPeriodicExamination` · `Adm_T_UserPatientProtocol` · `Adm_T_UserSickLeave` | Periyodik muayene (hekim `Hr_T_Doctor`'dan, diploma no seçimde `EditorScript.Copy` ile kopyalanır), hasta protokol defteri (`ProtocolType`/`ProtocolResult` statik lookup) ve istirahat listesi (`Days` tarihlerden `EditorScript.Days` ile hesaplanır). Entity'ler `IMultiTenant`; Sağlık Kartı sekmesinde (`HealthCardTab.tsx`) `UserId` filtresiyle gömülüdür. Yetki Work Experience ile aynıdır: okuma `AbpIdentity.Users`, ekleme/güncelleme/silme `AbpIdentity.Users.Update`. |
| **Identity › Users › Education & Skills lists** | `App.IdentityManagement.UserEducation` · `.UserLanguage` · `.UserSkill` · `.UserCertificate` (menüsüz alt listeler) | `Adm_T_UserEducation` · `Adm_T_UserLanguage` · `Adm_T_UserSkill` · `Adm_T_UserCertificate` | Eğitim geçmişi (`EducationLevel` kullanıcınınkiyle aynı değerler, `SchoolName`, `Department`, `StartDate`, `EndDate`, `IsGraduated`), yabancı dil (`SkillId` → `Languages` tipindeki `Hr_T_Skill`, `SkillLevelId` dilin tipine ait `Hr_T_SkillLevel`, cascade), yetenek (`SkillId` → `Languages` dışındaki tipler, `Level` 1-5 statik lookup, check constraint) ve yeterlilik belgesi (`CertificateId` → wizard tablosu `Hr_T_Certificate`, EF ilişkisi yok; `CertificateNo`, `IssuedBy`, `IssueDate`, `ExpiryDate`). Lookup'lar `LookupQueryValues.Tenant{LanguageSkill,SkillLevelBySkill,NonLanguageSkill,Certificate}Values`. `EducationSkillsTab.tsx`; yetki Work Experience ile aynıdır. |
| **Identity › Users › Family / Emergency lists** | `App.IdentityManagement.UserFamilyMember` · `.UserEmergencyContact` | `Adm_T_UserFamilyMember` · `Adm_T_UserEmergencyContact` | Eş ve çocuklar (`Relationship` `Spouse`/`Child`, ad, TC, doğum tarihi, `EmploymentStatus`, `IsInsuranceCovered`, `IsBenefitCovered`) ve acil durum kişileri (ad, `Relationship`, telefon). `FamilyEmergencyTab.tsx`; yetki Work Experience ile aynıdır. |
| **Identity › Users › Benefits** | `App.IdentityManagement.UserBenefit` | `Adm_T_UserBenefit` | Yan haklar (`BenefitType` — İK › Definitions › Benefit Type'tan `Code` değeri, `LookupQueryValues.BenefitTypeValues`, `Amount`, `StartDate`, `EndDate`, `Note`); Financial sekmesinde (`FinancialTab.tsx`). Yetki Work Experience ile aynıdır. |
| **Identity › Organization Units** | `Abp.Identity.OrganizationUnits` · `/admin/ous` | ABP OU tabloları | Birim ağacı. Ekranı `IsOrganizationUnit = true` yaparsan veri bu ağaca göre süzülür; bildirim alıcısı da OU olabilir. |
| **Identity › Audit Logs** | `App.IdentityManagement.AuditLogs` | `AbpAuditLogs` | İstek denetim kaydı + `AuditLogDetail` diyaloğu. "Kim ne zaman ne yaptı" isteği yeni tablo gerektirmez. |
| **Identity › Sessions** | `App.IdentityManagement.Sessions` | `AbpSessions` | Açık oturumlar; `UiEvalService.ApiKickUser` ile oturum düşürme (`lowcode` §9.13). |
| **Reports › Categories / Templates** | `App.Reports.Categories` · `App.Reports.ReportTemplates` | `Adm_T_ReportCategory` · `Adm_T_ReportTemplate` | Kendi düzeni olan çıktılar (fatura, teklif, sertifika, taahhütname). `Raporlar` kategorisindeki şablon `DynamicReport` ile basılan HTML'dir; satırdan `/admin/reports/DynamicReport/view/{Id}/{ListFormCode}?recordId=@Id` ile açılır, `{{AlanAdi}}` kayıtla dolar. MCP: `list_report_templates`, `save_report_template`; ekrana wizard `CommandColumns` butonuyla bağlanır (`lowcode` §9.12). Seed bayrakları açık. |
| **Videoroom** | `App.Videoroom` · `/admin/videoroom/dashboard` | `Adm_T_Videoroom` · `Adm_T_VideoroomParticipant` · `Adm_T_VideoroomAttandance` · `Adm_T_VideoroomChat` | Görüntülü oda, katılım ve sohbet kaydı. |
| **Forum** | menü kodu `App.Forum` · yetki `App.ForumManagement.Publish` · `/admin/forum` | `Sas_T_Forum*` | Forumun kullanıcı tarafı. |
Menüde görünmeyen ama route'u olan ekranlar: `/admin/dashboard`, `/admin/ai`,
`/admin/activityLog`, `/admin/changeLog`, `/admin/profile/{general,employee-info,leave,timesheet,performance,password,notification-settings}`,
`/admin/users/detail/:userId` (yetki `AbpIdentity.Users.Update`), `/admin/videoroom/list` ve
`/admin/videoroom/room/:id`, `/admin/reports/*`, `/admin/listform/{wizard,wizardManager}`,
`/admin/listform/edit/:listFormCode`, `/admin/developerkit/components/design/:id` ve dinamik ekran
rotaları. Custom component'lerin kendi `RoutePath`'leri (`/admin/roles`, `/admin/roles-list` gibi)
route tablosunda değil bileşen kaydındadır.
**Dinamik ekran rotaları** — üretilen her ListForm ekranı bunları kullanır, ayrıca route kaydı
açılmaz:
| Route | Bileşen | Ne açar |
| --- | --- | --- |
| `/admin/list/:listFormCode` | `views/list/List` | Liste (grid/card/pivot/tree/gantt/scheduler/todo — `LayoutJson`'a göre) |
| `/admin/form/:listFormCode` | `views/form/FormNew` | Yeni kayıt formu |
| `/admin/form/:listFormCode/:id` | `views/form/FormView` | Tek kayıt görüntüleme (`AllowDetail` bunu açar) |
| `/admin/form/:listFormCode/:id/edit` | `views/form/FormEdit` | Tek kayıt düzenleme |
| `/admin/chart/:listFormCode` | `views/list/Chart` | Yalnız grafik ekranı |
`/admin/pivot/...` diye bir route **yoktur**; pivot, liste ekranındaki bir görünümdür.
### 12.4 İK (Hr) menüsü
Kök menü `App.Hr` (`ModuleId = Hr`, `Order = 0`; Saas `-2` ve Administration `-1`'in yanında),
yetki grubu `App.Hr`. İK'ya ait her şey `Hr` ile başlar: tablo `Hr_{H|T}_*` (`MenuPrefix.Hr`),
ListForm kodu / menü kodu / yetki adı / dil anahtarı `App.Hr.*`, seed dosyası `data/App.Hr.*.json`,
seeder `ListFormSeeder_Hr.cs` (`SeedConsts.AppCodes.Hr`, `PlatformConsts.AppCodes.Hr`), entity
klasörü `Entities/Tenant/Hr/Definitions`. Tanımlar alt menüsü `App.Hr.Definitions`'tır
(`App.Platform.Definitions` başlığı); Organizasyon Şeması doğrudan `App.Hr` altındadır.
Aşağıdaki tabloda `Wizard` notu taşıyan ekranlar wizard ile üretilmiştir: entity'leri yoktur, tablo
`sql/object/Hr_T_*.sql`, ekran `wizard/{Ad}.json` dosyasındadır (`ListFormSeeder_Hr.cs`'te yer almazlar).
Araçlar (`App.Hr.Vehicles`) `App.Hr` altında ara menüdür; ara yetki aynı kodu taşır. Eğitimler
(`App.Hr.Training`) doğrudan `App.Hr` altındadır; Eğitim Tipleri/Konuları `App.Hr.Definitions` altındadır.
Intranet de bu moduldedir: menü `App.Hr.Intranet`, ekran/yetki kodları `App.Hr.Intranet.*`
(`SeedConsts.AppCodes.Intranet`), tablolar `Hr_T_{Announcement,Survey*,Social*,Event*}` ve mesajlaşma
`Hr_T_MessengerConversation*`, entity klasörü `Entities/Tenant/Hr/Intranet`. `Sas_T_IntranetWidget`
(Developer Kit › Intranet Widgets) Saas'ta kalır; kayıtlarının `PermissionName` değerleri `App.Hr.Intranet.*.Widget`'tır.
İK yetkilerinin `Modules` değeri `Sas|Hr`'dir: host (`Sas`) her zaman görür, kiracı yalnızca modül
listesinde `Hr` varsa görür (modül listesi boş kiracıda süzme yoktur — `MenuAppService`,
`RolesPermission.tsx`). Yetki ağacı: `App.Hr.Definitions` → `App.Hr.SkillType` (1),
`App.Hr.SkillLevel` (2), `App.Hr.Skill` (3), `App.Hr.Department` (4), `App.Hr.JobPosition` (5);
`App.Hr.OrgChart` grubun kökünde (2). ListForm ekranlarının alt yetkileri `.Create`, `.Update`,
`.Delete`, `.Export`, `.Import` (Skill Type'ta ayrıca `.Note`).
| Ekran | Kod / route | Tablo | Kapsam | Komponent üretirken ne işe yarar |
| --- | --- | --- | --- | --- |
| **Definitions › Skill Type** | `App.Hr.SkillType` (alt formlar `App.Hr.SkillLevel`, `App.Hr.Skill`) | `Hr_T_SkillType` · `Hr_T_SkillLevel` · `Hr_T_Skill` | tenant | Yetkinlik tanımı; bir ana ekrandan iki alt forma açılan **çoklu alt form** örneği. Seed `data/App.Hr.{SkillType,SkillLevel,Skill}.json`. |
| **Definitions › Department** | `App.Hr.Department` | `Hr_T_Department` | tenant | **Şube bazlı** departman ağacı: ilk sütun `BranchId` (Şube Adı; `LookupQueryValues.TenantBranchValues`, zorunlu), `ParentId` lookup'ı seçili şubenin departmanlarına süzülür (`BranchDepartmentValues`, cascade `BranchId`). `ParentId` ile **ağaç (TreeList) düzeninin** referansı; personel/onay ekranlarının departman lookup'ı. Tekil indeks `TenantId + BranchId + Name`. Seed `data/App.Hr.Department.json`. |
| **Definitions › Job Position** | `App.Hr.JobPosition` | `Hr_T_JobPosition` | tenant | Ünvan ağacı. Form sırası `BranchId` → `DepartmentId` (şubeye göre süzülür) → `Name` → `ParentId` (departmana göre süzülür); şube değişince departman ve üst görev temizlenir (`CascadeEmptyFields`). **İki kademeli cascade** örneği. Seed `data/App.Hr.JobPosition.json`. |
| **Work Calendar** | `App.Hr.WorkCalendar` | `Hr_T_WorkCalendar` | tenant | Resmi tatil ve çalışılmayan günler: `Name`, `StartDate`, `EndDate`, `DayType` (`Holiday`/`HalfDay`), `RecurrenceRule` (boş = tek sefer, `FREQ=YEARLY` = her yıl), `AllDay` (gizli, `true`), `IsActive`. Grid'e ek olarak **Takvim (Scheduler)** görünümü açıktır (`TextExpr = Name`). İzin ve puantaj hesabının tatil kaynağıdır. Wizard `wizard/Hr_WorkCalendar.json`, tablo `sql/object/Hr_T_WorkCalendar.sql`, seed `data/App.Hr.WorkCalendar.json` (sabit tarihli resmi tatiller her yıl tekrar eder; Ramazan ve Kurban bayramları hicri takvime göre kaydığı için 2026–2027 tarihleriyle tek seferlik girilir, sonraki yıllar ekrandan eklenir). |
| **Definitions › Leave Type** | `App.Hr.LeaveType` | `Hr_T_LeaveType` | tenant | İzin türü: `Code`, `Name`, `Gender` (`All`/`Male`/`Female`), `MaxDaysPerYear`, `IsPaid`, `DeductsFromAnnualLeave`, `RequiresDocument`, `AllowHalfDay`, `BalanceExceedAction` (`Block`/`Warn`), `IsActive`. İzin Yönetimi'nin tür kuralları; `Sick` kodlu tür Sağlık Kartı istirahatiyle eşlenir. Wizard `wizard/Hr_LeaveType.json`, tablo `sql/object/Hr_T_LeaveType.sql`, seed `data/App.Hr.LeaveType.json` (Türkiye standart izin türleri). |
| **Definitions › Expense Type** | `App.Hr.ExpenseType` | `Hr_T_ExpenseType` | tenant | Masraf türü: `Code`, `Name`, `DefaultAmount`, `CurrencyId` (`Sas_H_Currency` lookup'ı), `IsTaxable`, `IsSgkSubject`, `RequiresReceipt`, `IsActive`. Masraf beyanı ekranlarının lookup'ı. Wizard `wizard/Hr_ExpenseType.json`, tablo `sql/object/Hr_T_ExpenseType.sql`, seed `data/App.Hr.ExpenseType.json`. |
| **Definitions › Benefit Type** | `App.Hr.BenefitType` | `Hr_T_BenefitType` | tenant | Yan hak türü: `Code`, `Name`, `DefaultAmount`, `CurrencyId`, `IsTaxable`, `IsSgkSubject`, `IsActive`. Kodu kullanıcının yan hak kaydında (`Adm_T_UserBenefit.BenefitType`, Identity › Users › Benefits) değer olarak saklanır; kod değiştirilirse eski kayıtlar eşleşmez. Wizard `wizard/Hr_BenefitType.json`, tablo `sql/object/Hr_T_BenefitType.sql`, seed `data/App.Hr.BenefitType.json`. |
| **Shift** | `App.Hr.Shift` | `Hr_T_Shift` | tenant | Vardiya: `Name`, `StartTime`/`EndTime` (`HH:mm`), `BreakMinutes`, `LateToleranceMinutes`/`EarlyLeaveToleranceMinutes` (boşsa Ayarlar › Puantaj), `WorkingHours`, `IsNightShift`, `IsActive`. Seed `data/App.Hr.Shift.json`. |
| **Event Log** | `App.Hr.EventLog` | `Hr_T_EventLog` | tenant | Olay günlüğü: vardiya amiri / güvenlik odası / gözlem odası gibi kaynakların (`LogSource` statik lookup) gün içi kayıtları — `BranchId`, `UserId` (olayı yaşayan), `EventDate`/`EventTime`, `Importance`, `ShiftId` (`Hr_T_Shift`), `Description`. Wizard `wizard/Hr_EventLog.json`, tablo `sql/object/Hr_T_EventLog.sql`. |
| **Definitions › Training Type / Subject** | `App.Hr.TrainingType` · `App.Hr.TrainingSubject` | `Hr_T_TrainingType` · `Hr_T_TrainingSubject` | tenant | Eğitim türü (`Name`, `IsActive`) ve konusu (`TrainingTypeId`, `Name`, `IsEvaluated`, `IsActive`). Wizard; seed `data/App.Hr.{TrainingType,TrainingSubject}.json`. |
| **Training** | `App.Hr.Training` (alt formlar `App.Hr.TrainingParticipant`, `App.Hr.TrainingFile`) | `Hr_T_Training` · `Hr_T_TrainingParticipant` · `Hr_T_TrainingFile` | tenant | Eğitim kaydı: tür → konu cascade (`TrainingSubjectId` ← `TrainingTypeId`), şube, tarih/saat + `TotalHours`, `Status` (`Open`/`Closed`), eğitmen/yer/kurum, kazandırdığı yetkinlikler (`SkillIds` — `dxTagBox`, `Hr_T_Skill`), maliyet kalemleri + `CostTotal`. Katılımcı alt formu kullanıcıyı `AbpUsers`'tan seçer, `HireDate`/`DepartmentId`/`JobPositionId` kopyalanır; `AttendanceStatus`, değerlendirme/sınav puanı. Dosya alt formu `DocumentTypeId` (`Hr_T_DocumentType`) taşır. Katılım belgesi `Raporlar` kategorisindeki "Katılım Belgesi" şablonudur (`App.Hr.TrainingParticipant`). Wizard. |
| **Vehicles › Vehicle Document / Expense Definition / Destination** | `App.Hr.VehicleDocument` · `App.Hr.VehicleExpenseDefinition` · `App.Hr.VehicleDestination` | `Hr_T_VehicleDocument` · `Hr_T_VehicleExpenseDefinition` · `Hr_T_VehicleDestination` | tenant | Araç modülünün tanımları: belge türü (`DocumentGroup`, `DocumentType`, `Name`), masraf kalemi (`ExpenseType`, `Name`), gidilecek yer (`Name`, `Distance`); hepsinde `IsActive`. Wizard; belge ve masraf tanımları seed'lidir (`data/App.Hr.{VehicleDocument,VehicleExpenseDefinition}.json`). |
| **Vehicles › Vehicle** | `App.Hr.Vehicle` (alt form `App.Hr.VehicleFile`) | `Hr_T_Vehicle` · `Hr_T_VehicleFile` | tenant | Araç kartı: şube, marka/model/kasa, `PlateNumber`, `CurrentKm`, `Status`, muayene/sigorta/egzoz/takograf başlangıç–bitiş tarihleri. Dosya alt formu belge türünü `Hr_T_VehicleDocument`'tan seçer. Wizard. |
| **Vehicles › Driver** | `App.Hr.Driver` (alt formlar `App.Hr.DriverDocument`, `.DriverPenalty`, `.DriverAccident`) | `Hr_T_Driver` · `Hr_T_DriverDocument` · `Hr_T_DriverPenalty` · `Hr_T_DriverAccident` | tenant | Şoför = kullanıcı (`UserId` → `AbpUsers`) + en çok beş araç (`Vehicle1Id`…`Vehicle5Id`) + `TotalKm`; belge, ceza (`Amount`) ve kaza kayıtları alt formdur, ekli dosyayla. Wizard. |
| **Vehicles › Vehicle Expense** | `App.Hr.VehicleExpense` (alt form `App.Hr.VehicleExpenseLine`) | `Hr_T_VehicleExpense` · `Hr_T_VehicleExpenseLine` | tenant | Araç masraf faturası (`VehicleId`, `InvoiceNo`, `InvoiceDate`, `DestinationId`, belge görseli `dxImageUpload`) ve satırları (`ExpenseDefinitionId`, `Quantity`, `Amount`, `DiscountRate`, `NetAmount`). **Fiş + satır** kalıbı. Wizard. |
| **Vehicles › Vehicle Movement** | `App.Hr.VehicleMovement` | `Hr_T_VehicleMovement` | tenant | Araç çıkış/giriş kaydı: çıkışta şube, tarih/saat, km, gönderen/güvenlik/şoför kullanıcıları, araç, gidilecek yer, sebep; girişte güvenlik, şube, tarih/saat, km; `TravelledKm`, `DurationMinutes`, `ExpectedKm` hesaplanır, `Status` (`Arrived`/`Left`). Wizard. |
| **Definitions › Penalty Type** | `App.Hr.DisciplinaryPenaltyType` | `Hr_T_DisciplinaryPenaltyType` | tenant | Disiplin cezası türü: `Name`, `Severity` (1–5), `Description`, `IsActive`. Karar alt formunun lookup'ı (aktifler, ağırlık sırasıyla). Wizard; seed `data/App.Hr.DisciplinaryPenaltyType.json` (sözlü/yazılı uyarı, ihtar, kınama, ücret kesintisi, fesih). |
| **Disciplinary Case** | `App.Hr.DisciplinaryCase` (alt formlar `App.Hr.DisciplinaryDefense`, `.DisciplinaryDecision`, `.DisciplinaryFile`) | `Hr_T_DisciplinaryCase` · `Hr_T_DisciplinaryDefense` · `Hr_T_DisciplinaryDecision` · `Hr_T_DisciplinaryFile` | tenant | Olay tutanağı: personel (`UserId` → `AbpUsers`, `DepartmentId`/`JobPositionId` kopyalanır), olay tarihi/saati/yeri, `Subject`, `IncidentDescription` (`dxHtmlEditor`), tanıklar (`WitnessUserIds` — `dxTagBox`), `ReportedById`, `Status` (`Open`/`AwaitingDefense`/`Decided`/`Closed`; Todo panosu bu kolonla). `CaseNo` (`DSP-{yyyy}-{00001}`, kiracı ve yıl bazında) `InsertAfterCommand` ile SQL'de üretilir — `Sequence` kiracıya kopyalanmadığı için numaratör kullanılmaz. Alt formlar: savunma (`RequestDate`, `DueDate` = talep + 2 gün, `ResponseDate`, `DefenseText`, `Status`), karar (`PenaltyTypeId`, karar/yürürlük/tebliğ tarihi, `DeductionAmount`, `Status` `Draft`/`Final`, onay süreci yok), belge (`DocumentTypeId` → `Hr_T_DocumentType`, dosya). Alt form yetkileri `App.Hr.DisciplinaryCase` altındadır. Wizard. |
| **Definitions › Candidate Source / Rejection Reason / Evaluation Criterion** | `App.Hr.CandidateSource` · `App.Hr.RejectionReason` · `App.Hr.EvaluationCriterion` | `Hr_T_CandidateSource` · `Hr_T_RejectionReason` · `Hr_T_EvaluationCriterion` | tenant | İşe alım tanımları: aday kaynağı (`Name`), ret sebebi (`Name`, `RejectedBy` = `Company`/`Candidate`), değerlendirme kriteri (`Name`, `Weight` — mülakat puanının ağırlığı). Hepsi `IsActive` + Toplam/Aktif/Pasif widget'ı, `SeedData` açık (`data/App.Hr.{CandidateSource,RejectionReason,EvaluationCriterion}.json`). Wizard. |
| **Recruitment › Staff Request** | `App.Hr.StaffRequest` | `Hr_T_StaffRequest` | tenant | Kadro talebi: `RequestNo` (`KT-YYYY-00001`, insert sonrası SQL), şube → departman → pozisyon cascade, `Headcount`, `EmploymentType`, `Reason` (`NewPosition`/`Replacement` + `ReplacedUserId`), ücret aralığı. **Grid Workflow** onayı: Start → Approval (`Onay Bekliyor`) → Inform → End `Onaylandı`/`Reddedildi`; durum `ApprovalStatus`, onaycı `ApprovalUser`. Onaycı düğümün `Approver` değeridir (seed'de platform admin e-postası — ListForm › Workflow sekmesinden değiştirilir); motor onaycıyı kayıttaki alandan okumaz. Wizard. |
| **Recruitment › Job Posting** | `App.Hr.JobPosting` (alt form `App.Hr.JobApplication`) | `Hr_T_JobPosting` | tenant | İlan: `PostingNo` (`ILN-…`), `StaffRequestId` (yalnızca `ApprovalStatus = 'Onaylandı'` talepler; seçilince şube/departman/pozisyon/adet/çalışma şekli `copy` ile dolar), `Status` (`Draft`/`Published`/`Closed`/`Cancelled`), yayın kanalları (`SourceIds`, `dxTagBox`), açıklama/nitelikler HTML. Wizard. |
| **Recruitment › Candidate** | `App.Hr.Candidate` (alt form `App.Hr.JobApplication`) | `Hr_T_Candidate` | tenant | Aday havuzu: kişisel bilgiler, eğitim/deneyim, `SkillIds` (`Hr_T_Skill`), fotoğraf, `CvDocument` (`dxImageUpload`, PDF/DOC kabul eder — görsel olmayan dosyada belge ikonu, tıklanınca blob olarak açılır), **KVKK**: `KvkkConsent` zorunlu (form + tabloda `CHECK (KvkkConsent = 1)`), `KvkkConsentDate`, `KvkkConsentMethod`, imzalı belge. E-posta kiracı içinde tekildir (filtreli unique index). `UserId` personel kaydıdır: kullanıcıyı BT elle açar, İK aday kaydını ona bağlar ("Personele Dönüşen" kartı bunu sayar). Satır butonu KVKK Açık Rıza Formu (`DynamicReport`). Wizard. |
| **Recruitment › Application** | `App.Hr.JobApplication` (alt formlar `App.Hr.Interview`, `App.Hr.JobOffer`) | `Hr_T_JobApplication` | tenant | Aday × ilan başvurusu (çift tekil). `Stage`: `Applied` → `Screening` → `Interview` → `Offer` → `Hired` / `Rejected`; **Todo (kanban) görünümü** aşama sütunlarıyla. Mülakat eklenince `Interview`, teklif eklenince `Offer`, teklif `Accepted` olunca `Hired`'a ilerler (after-command SQL, geri gitmez; insert sonrası komutlar yalnızca eklenen satıra `@Id` ile çalışır). `CandidateName`/`PostingTitle` kopyalanır (kanban başlığı). Ret sebebi yalnızca `Rejected` aşamasında düzenlenir. Alt form olduğu için widget yok. Wizard. |
| **Recruitment › Interview** | `App.Hr.Interview` (alt form `App.Hr.InterviewScore`) | `Hr_T_Interview` · `Hr_T_InterviewScore` | tenant | Mülakat planlama: tür, başlangıç/bitiş (**Scheduler** görünümü), mülakatçı, durum, öneri. Kayıt eklenince mülakatçıya `IseAlimMulakatAtandi` bildirimi SQL ile yazılır (tipin `Custom` kuralları kanalı belirler, metin `App.Hr.Interview.Notification.*`). Puan alt formu kriter başına 1–5; `OverallScore` = Σ(puan × ağırlık) / Σ ağırlık, başvurunun `AverageScore`'u mülakatların ortalaması. Wizard. |
| **Recruitment › Job Offer** | `App.Hr.JobOffer` | `Hr_T_JobOffer` | tenant | Teklif: `OfferNo` (`TKL-…`), başvuru seçilince pozisyon bilgileri ilandan `copy` ile dolar, brüt ücret + para birimi, yan haklar (`Hr_T_BenefitType` kodları), `Status` (`Draft`/`Sent`/`Accepted`/`Declined`/`Expired`, yanıt tarihi otomatik). İç onay **Grid Workflow** (Kadro Talebi ile aynı akış). Onaylı satırda **Teklif Mektubu** butonu (`DynamicReport`, `Raporlar` kategorisi). Wizard. |
| **Performance Management** | `App.Hr.Performance` dalı (yalnız İK): `PerformancePeriod`, `PerformanceReview`, `PerformanceCalibration`, `DevelopmentPlan`; tanım `App.Hr.PerformanceRating` (İK › Tanımlar). Çalışana ait `MyPerformanceReview`, `TeamPerformanceReview`, `My360Feedback` menüsüzdür; Intranet › Dashboard'daki custom widget'lardan (`MyPerformanceWidget`, `TeamPerformanceWidget`, `My360FeedbackWidget`) açılır; çalışanınkiler profilin **Performansım** sekmesinde de görünür | `Hr_T_PerformanceRating` · `Hr_T_PerformancePeriod` · `Hr_T_PerformancePeriodSkill` · `Hr_T_PerformanceReview` · `Hr_T_PerformanceGoal` · `Hr_T_PerformanceCompetency` · `Hr_T_PerformanceFeedback` · `Hr_T_PerformanceFeedbackScore` · `Hr_T_DevelopmentPlan` | tenant | Dönem → değerlendirme (hedef KPI/OKR + yetkinlik `Hr_T_Skill`/`Hr_T_SkillLevel` + isteğe bağlı 360) → kalibrasyon → gelişim planı. **Aynı tabloya persona ekranları**: İK (tümü), çalışan (`EmployeeId = '@USERID'`), yönetici (`ManagerId = '@USERID'`), 360 değerlendiricisi (`ReviewerId = '@USERID'`); kilitli alanlar İK ekranının yetkisine bağlıdır (sunucu düşürür), satır sahipliği ve aşama kuralı `Hr_S_PerformanceGuard` (before-command). Hesap ve durum geçişi `Hr_S_PerformanceReviewRecalc` / `Hr_S_PerformanceReviewAfterSave`; tamamlanan değerlendirme `Adm_T_UserSkill`'e yazar. Widget'lar veriyi ekranın kendi sorgusundan (`list-form-select/select`) okur. Çalışma şekli ve kararlar: `configs/seeds/docs/modules/App.Hr.md` › Performans Yönetimi. Wizard + Custom Component (pano kutusu). |
| **Leave Management** | `App.Hr.Leave` dalı (yalnız İK): `LeaveRequest` (Grid Workflow: Müdür Onayı `@ManagerUserNames` → İK Onayı; üst pozisyonu olmayan çalışanda müdür adımı `RequiresManagerApproval` karşılaştırmasıyla atlanır), `LeaveBalance` (alt formlar `LeaveBalanceAdjustment`, `LeaveBalanceYear`). Çalışana ait `MyLeaveRequest`, `MyLeaveBalance` ve müdüre ait `TeamLeaveRequest` menüsüzdür; Intranet › Dashboard'daki `MyLeaveWidget` / `TeamLeaveWidget`'tan açılır; çalışanınkiler profilin **İzinlerim** sekmesinde de görünür; kullanıcı detayında **İzinler** sekmesi | `Hr_T_LeaveRequest` · `Hr_T_LeaveBalanceAdjustment` · görünümler `Hr_V_LeaveBalanceYear` · `Hr_V_LeaveSummary` | tenant | Gün sayısı `Hr_F_LeaveWorkDays` (Ayarlar › İnsan Kaynakları › İzin çalışma günleri + `Hr_T_WorkCalendar`); bakiye İş Kanunu md.53 kademeleri (ayarlardan) + devir + düzeltme; müdür = departmanın en üst pozisyonunun sahibi. Doğrulama, müdür çözümü, rapor ↔ `Adm_T_UserSickLeave` eşlemesi ve bildirim `Hr_S_LeaveRequestAfterSave`; satır koruması `Hr_S_LeaveRequestGuard`; Sağlık Kartı tarafı `Hr_S_LeaveSickLeaveSync`. SQL'den ayar okuma `Hr_F_HrSetting` (kiracı → genel → varsayılan). Çalışma şekli: `configs/seeds/docs/modules/App.Hr.md` › İzin Yönetimi. Wizard + Custom Component + küçük UI/motor eki. |
| **Timesheet & Attendance** | `App.Hr.Attendance` dalı (yalnız İK): `AttendanceLog` (ham okutma; Excel import), `Timesheet` (çalışan × gün; düzenlenen gün `IsManual`), `TimesheetMonthly` (salt okunur aylık özet, alt form `Timesheet` `Id → PeriodKey`), `TimesheetPeriod` (ay yeniden hesap / kapanış). Çalışana ait `MyTimesheet` (+ alt form `MyTimesheetDay`) menüsüzdür; Intranet › Dashboard'daki `MyTimesheetWidget`'tan açılır; profilin **Puantajım** sekmesinde de görünür; kullanıcı detayında **Puantaj** sekmesi | `Hr_T_AttendanceLog` · `Hr_T_Timesheet` · `Hr_T_TimesheetPeriod` · görünümler `Hr_V_AttendanceShift` · `Hr_V_TimesheetMonthly` | tenant | Hesap `Hr_S_TimesheetCalculate` (gün tipi: rapor > ücretsiz izin > `Hr_T_WorkCalendar` tatili > hafta tatili (İzin çalışma günü ayarları) > ücretli izin > okutma > eğitim katılımı (`Training`) > varsayılan çalışma / devamsız; izinli günde `LeaveTypeId`, eğitim gününde `TrainingId`; mola `MAX(vardiya, okutulan)`; geç/erken vardiya toleransı aşılırsa tamamı; fazla mesai haftalık 45 saati aşan kısım + tatil çalışması). Kişi eşlemesi `AbpUsers.PdksCardNo` / `RegistrationNo`; kartı olan çalışan ilk PDKS okutmasından itibaren okutmayla, diğerleri elle (kayıtsız gün `IsAssumed`) takip edilir. Tetikler: hareket kaydı `Hr_S_AttendanceProcess`, PDKS Custom Endpoint `Hr_S_AttendanceImport` (`/hr/attendance/`), dönem `Hr_S_TimesheetPeriodAfterSave`, gece işi `Hr_S_TimesheetNightly`, izin/rapor prosedürleri, `Hr_S_TrainingSync`, `Hr_S_WorkCalendarAfterSave`. Modüller arası çakışma kuralları (raporlu gün devamsız değildir, rapora denk gelen izin günü bakiyeye döner, kapalı aya izin/rapor yazılmaz) `App.Hr.md` › Puantaj ve Devam Takibi §7.1'dedir. Ayarlar `App.HrSettings.Attendance.*`. Çalışma şekli: `configs/seeds/docs/modules/App.Hr.md` › Puantaj ve Devam Takibi. Wizard + Custom Component + Custom Endpoint + zamanlanmış iş + küçük UI eki. |
| **Org Chart** | `App.Hr.OrgChart` · `/admin/organization` | `Hr_T_Department` · `Hr_T_JobPosition` | tenant | Departman/ünvan verisinin organizasyon şeması görünümü (özel React sayfası `hr/OrgChart`, veri `api/app/org-chart`). Menü ve route aynı yetkiyi ister. |
| **Intranet › Announcement** | `App.Hr.Intranet.Announcement` (alt formlar Like/Comment) | `Hr_T_Announcement` · `Hr_T_AnnouncementLike` · `Hr_T_AnnouncementComment` | tenant | Duyuru + beğeni + yorum: **ana kayıt / alt etkileşim** kalıbının hazır örneği. |
| **Intranet › Survey** | `App.Hr.Intranet.Survey` (alt form Question → Option; Response → Answer) | `Hr_T_Survey` · `Hr_T_SurveyQuestion` · `Hr_T_SurveyQuestionOption` · `Hr_T_SurveyResponse` · `Hr_T_SurveyAnswer` | tenant | Anket motoru; soru–seçenek–yanıt zinciri **iki seviyeli alt form** ihtiyacının referansıdır. |
| **Intranet › Social Post** | `App.Hr.Intranet.SocialPost` | `Hr_T_SocialPost` · `Hr_T_SocialComment` · `Hr_T_SocialLike` (+ `SocialPollOption`, `SocialMedia`, `SocialLocation`) | tenant | Kurum içi akış; `IsLiked`, `IsOwnPost` gibi hesaplanmış kolonların view içinde üretilmesine örnek. Alt formlar: `App.Hr.Intranet.SocialMedia`, `SocialComment`, `SocialLike` (`Id` → `SocialPostId`); anket seçenekleri (`App.Hr.Intranet.SocialPollOption`) medyaya bağlı olduğu için `SocialMedia` ekranının alt formudur (`Id` → `SocialMediaId`). |
| **Intranet › Event Type / Category / Event** | `App.Hr.Intranet.Events.EventType` · `.EventCategory` · `.Event` | `Hr_T_EventType` · `Hr_T_EventCategory` · `Hr_T_Event` · `Hr_T_EventPhoto` · `Hr_T_EventLike` · `Hr_T_EventComment` | tenant | Etkinlik yönetimi; foto ve katılımcı sayacı olan kayıt kalıbı. `EventLike`/`EventComment` alt formdur, `EventPhoto` menüde yer almaz; yetkileri `App.Hr.Intranet.Events.Event` altındadır. `Hr_T_EventPhoto` tablosu `sql/object/Hr_T_EventPhoto.sql` ile oluşturulur. |
| **Intranet › Documents** | `App.Hr.Intranet.Document` · `/admin/intranet-documents` | Dosya yöneticisi (`intranet` / `intranet\|_passive` klasörleri) | tenant | Kurum içi doküman paylaşımı — aktif/pasif klasör, yükleme, taşıma, toplu silme. Doküman listesi isteği yeni tablo gerektirmez. |
**Şube bilgisi:** `Department.BranchId` ve `JobPosition.BranchId` (`Guid`, **zorunlu** — kolon `NOT NULL`, formda
zorunlu alan; değer `Sas_T_Branch.Id`) şubeyi taşır; ekranlar `IsBranch = false`'tur (satırlar kullanıcının şube atamasına göre süzülmez, şube
sütunu veridir). Host varsayılan şubesinin kimliği sabittir
(`PlatformConsts.Branches.BranchIdDefaultValue`; `PlatformIdentityDataSeeder` host şubesi için bu
kimliği kullanır, kiracı şubeleri yeni GUID alır);
seed edilen departman ve pozisyon satırları `BranchId` ile bu şubeye bağlanır. Yeni seed satırı
eklerken `BranchId` de yazılır.
### 12.5 ListForm olmayan (özel React) ekranlar
Bu ekranlar `Sas_H_Route` kayıtlarıyla yönetilir (seed:
`configs/seeds/data/App.Menus.Routes.json`). Yenisini eklemek **kod yazmak demektir** — karar
sırasının son adımıdır ve Core'dur (yalnızca "core sisteme ekle" komutuyla, `lowcode` §0.8). Rotalı bir sayfa ihtiyacının
çerçeve içi karşılığı `RoutePath` taşıyan bir custom component'tir.
Bir route kaydının alanları: `Id`, `Key`, `Path`, `ComponentType`, `ComponentPath` (`@/views/...`),
`RouteType`, `Authority` (virgülle ayrılmış yetki listesi) ve SEO alanları `SeoTitle`,
`SeoDescription`, `SeoKeywords`, `SeoOgImage`, `SeoCanonicalUrl`, `SeoNoIndex`,
`SeoIncludeInSitemap`, `SeoChangeFrequency`, `SeoPriority`, `SeoStructuredData` (Custom Component
kaydı aynı `Seo*` kümesini taşır).
| `routeType` | Anlam |
| --- | --- |
| `public` | Oturum gerekmez — genel siteye açık sayfalar |
| `authenticated` | Kimlik akışı sayfaları (giriş, kayıt, parola, doğrulama) |
| `protected` | Oturum zorunlu; `authority` doluysa ayrıca yetki kontrolü yapılır |
`authority: []` **yetki kontrolü yok** demektir; ekranın kendi yetkisi varsa kontrol
bileşenin/ListForm'un içindedir. Custom component route'ları da böyle üretilir — bu yüzden
görünürlük `checkPermission` ile bileşen içinde kurulur (`lowcode` §7.5).
| Route | Bileşen | Yetki |
| --- | --- | --- |
| `/admin/menuManager` | `views/menu/MenuManager` | `App.Menus.Manager` |
| `/admin/listform/wizard` | `listForm/wizard/Wizard` | `App.Listforms.Wizard` |
| `/admin/listform/wizardManager` | `listForm/wizard/WizardFileManager` | `App.Listforms.Wizard` |
| `/admin/listform/edit/:listFormCode` | `listForm/edit/FormEdit` | `[]` — yetki ListForm tarafında |
| `/admin/sqlQueryManager` | `developerKit/SqlQueryManager` | `App.SqlQueryManager` |
| `/admin/developerkit/components/design/:id` | `ComponentCodeLayout` (liste tarafı ListForm ekranıdır) | `App.DeveloperKit.CustomComponents` |
| `/admin/reports/:report/{view,design}/:id?/:listFormCode?` | `DevexpressReportViewer` · `DevexpressReportDesigner` | — |
| `/admin/settings` | `settings/Settings` | `App.Setting` |
| `/admin/files` | `admin/files/FileManager` | `App.Files` |
| `/admin/intranet-documents` | `intranet/documents/IntranetDocuments` | `App.Hr.Intranet.Document` |
| `/admin/ous` · `/admin/organization` | `organization-unit/OrganizationUnits` · `hr/OrgChart` | `Abp.Identity.OrganizationUnits` · `App.Hr.OrgChart` (menü ile aynı — §12.4) |
| `/admin/forum` | `forum/Forum` | `App.ForumManagement.Publish` |
| `/admin/videoroom/{dashboard,list,room/:id}` | `admin/videoroom/{Dashboard,RoomList,RoomDetail}` | `App.Videoroom{,.List,.Detail}` |
| `/admin/ai` | `ai/Assistant` | `App.Definitions.AiBot.Asistant` |
| `/admin/dashboard` · `/admin/activityLog` · `/admin/changeLog` · `/admin/profile/*` | `Dashboard` · `admin/activityLog/ActivityLog` · `version/ChangeLog` · `admin/profile/Profile` | `[]` |
| `/admin/users/detail/:userId` | `admin/user-management/Details` | `AbpIdentity.Users.Update` |
**Oturum gerektirmeyen sayfalar** — genel site. `/home` · `/about` · `/contact` route tablosunda
**yoktur**: `/admin/` ile başlamayan `RoutePath`'li Custom Component otomatik public rota olur
(aynı yolu paylaşan fiziksel rotayı gölgeler). Bir "tanıtım sayfası" isteğinde yeni React sayfası
yazma; tasarımcıda yeni bir Custom Component + `Pub` menü kaydı (`App.Public.Header` altı) üret
(`README.md` §10.4). Üst ve alt bilgi rotasız bileşenlerdir (`PublicHeader`, `PublicFooter`,
`RoutePath = NULL`); public bileşenlerin hepsi tasarımcı belgesidir (`sourceMode: visual`).
| Route | Bileşen | Veri |
| --- | --- | --- |
| `/home` · `/about` · `/contact` | Custom Component (`PublicHomePage` · `PublicAboutPage` · `PublicContactPage`) | `data/App.DeveloperKit.CustomComponents/{Id}.json` · SEO bileşen kaydında |
| (rotasız) | Custom Component (`PublicHeader` · `PublicFooter`) | Üst menü + alt bilgi hızlı bağlantıları: `Pub` menüsü `App.Public.Header` (uygulama konfigürasyonunun `publicMenus` alanı) |
| `/products` · `/checkout` · `/payment` · `/success` | `views/public/{Products,Checkout,Payment,Success}` | `Sas_H_Product` · `Sas_H_PaymentMethod` · `Sas_H_InstallmentOption` · `Sas_H_Order` |
| `/blog` · `/blog/:id` | `views/public/{Blog,BlogDetail}` | `Sas_H_BlogPost` · `Sas_H_BlogCategory` |
| `/demo` | `views/public/Demo` | `Sas_H_Demo` |
| `/access-denied` | `views/AccessDenied` | — |
Ziyaretçiye açık her yolun SEO alanları ayrı bir tabloda değil, yolu tanımlayan kaydın
üzerindedir: `Route` satırının ya da Custom Component'in `Seo*` kolonları; genel varsayılanlar
`App.SiteManagement.Seo.*` ayarlarındadır. `sitemap.xml` `Pub` menülerini, aktif custom component
rotalarını ve yayınlanmış blog yazılarını toplar (`SeoAppService`). Yeni bir public yol üreten iş bu
alanları da doldurur (`lowcode` §0.9).
**Kimlik akışı** (`routeType: authenticated`): `/login`, `/register`, `/forgot-password`,
`/reset-password`, `/confirm`, `/confirm/:userId/:token`, `/extend-login` →
`views/auth/*`. Bunlara dokunma; parola ve doğrulama akışları ABP tarafındadır.
### 12.6 Talep → hangi menü
§0'daki katman taraması bu tabloyla eşleşir; "kod yazmamız gerekir" demeden önce buraya bak.
| Talepte geçen | Menü / ekran | Ne yapılır |
| --- | --- | --- |
| "numara otomatik gelsin" | Administration › Definitions › Sequence | Kayıt aç, alan varsayılanına `CustomValueType: 5` |
| "her gece / saat başı çalışsın" | Saas › Background Workers | `WorkerType: 2` + `BeforeSp`; prosedür `sql/object/` altına |
| "bilgilendirme gitsin" | Administration › Bildirimler (+ MailQueue §11) | Tür + kural (8 kanal); tablolu/ekli e-posta ise MailQueue |
| "elden bildirim/duyuru gönderelim" | Administration › Bildirimler › Bildirimler (+ Şablonlar) | Ekleme formu = elle gönderim; hazır metin için şablon kaydı |
| "onaya düşsün" | `WorkflowJson` + `ListFormWorkflow` (örnek: Sales Orders) | Onay düğümleri; `Inform` düğümü e-posta gönderir |
| "çıktısı alınsın / fatura / sertifika basılsın" | Administration › Reports | Hazır `DynamicGrid` ya da `Raporlar` kategorisinde `DynamicReport` şablonu (`save_report_template`) |
| "dosya eklensin" | Saas › Developer Kit › Files | Dosya/görsel editörü; ayrı depolama kurma |
| "fotoğraf çekilsin / barkod okutulsun / konum alınsın" | Alan editörü (§9.8 tablosu) · tasarımcıda `BarcodeScanField` | `Capture`, `@SCAN_BARCODE`, `BarcodeScanDialog`, `geoTo`; yeni editör yazma |
| "barkod/QR basılsın" | Visual Designer › `BarcodeViewer` | `value`'yu `Form` kolonuna bağla |
| "başka veritabanından gelsin" | Saas › Data Source | Yeni DataSource + ekranın `DataSourceCode` alanı |
| "ayarlanabilir olsun" | Saas › Setting Definitions | Tanım aç, değeri `/admin/settings`'ten yönet |
| "İngilizcesi de olsun" | Saas › Languages | Dil anahtarı + `data/App.Languages.LanguageText.json` |
| "aramada çıksın" | Saas › Definitions › Global Search | `System`/`Group`/`Term`/`Weight`/`Url` kaydı |
| "sadece şu rol görsün" | Administration › Identity › Roles / Permissions | Yetki kodu + ekranın `PermissionJson`'ı |
| "herkes yalnızca kendi kayıtlarını görsün" / "şu rol şu bölgeyi görsün" | ListForm editörü › Kişiselleştirme | `ServerWhere` kaydı (herkes/rol/kullanıcı; §2.5) ya da `DefaultFilter` |
| "intranet ana sayfasına kutu eklensin" | Saas › Developer Kit › Intranet Widgets | `Custom` kaynaklı widget + custom component |
| "çalışan kendi bilgisini / izinini / puantajını görsün" | Profil (`/admin/profile/*`) + Intranet panosu | Menüsüz `App.Hr.My*` persona ekranı + widget (`lowcode` §0.12.1); profil sekmesi sabit listedir, yenisi kod değişikliğidir |
| "şube bazlı olsun" | Saas › Branches | `IsBranch = true` |
| "birim ağacına göre süzülsün" | Administration › Identity › Organization Units | `IsOrganizationUnit = true` |
| "mesai dışında girilemesin" / "sadece ofis IP'si" | Administration › Restrictions | Work Hour · IP Restriction kaydı |
| "kim değiştirmiş görelim" | Administration › Identity › Audit Logs | Hazır ekran; kayıt notu için `ShowNote` |
| "menüye eklensin" | Saas › Menus › Manager | Menü kaydı + yetki + route eşlemesi |
| "yapay zekâ ajanı sorgulasın / veri çeksin" | Saas › DeveloperKit › MCP Servers | Kayıt aç, `AllowedTools`/tenant kapsamını ayarla; yeni endpoint/kod yok |
| "yapay zekâ ajanı ekran/bileşen geliştirsin" | Saas › DeveloperKit › MCP Servers | Yalnızca host kullanıcısı: `App.DeveloperKit.McpServers.Invoke` + `.Write` + Wizard / Custom Components yetkilerini ver; geliştirme araçları `AllowedTools` ile açılıp kapatılır. Kiracı MCP'de veri okur ve yazar, geliştirme yapamaz |
| "ülke/il/ilçe, para birimi, birim, sektör" | Saas / Administration › Definitions | Var olan tabloya lookup bağla, yenisini üretme |
| "departman, pozisyon/ünvan, yetkinlik, organizasyon şeması" | İK › Definitions · İK › Org Chart | Var olan `Hr_*` tablosuna lookup bağla (departman lookup'ı şubeye göre süzülebilir — `BranchDepartmentValues`); yenisini üretme |
| "izin türü, masraf türü, yan hak türü, vardiya, resmi tatil / çalışma takvimi" | İK › Definitions · İK › Work Calendar · İK › Shift | Var olan `Hr_T_WorkCalendar` / `Hr_T_LeaveType` / `Hr_T_ExpenseType` / `Hr_T_BenefitType` / `Hr_T_Shift` tablosuna lookup bağla; yenisini üretme |
| "eğitim / katılımcı, araç / şoför / araç masrafı / araç çıkış-giriş, olay günlüğü, disiplin / tutanak / savunma / ihtar" | İK › Trainings · İK › Vehicles · İK › Event Log · İK › Disciplinary Case | Var olan `Hr_T_Training*` / `Hr_T_Vehicle*` / `Hr_T_Driver*` / `Hr_T_EventLog` / `Hr_T_Disciplinary*` ekranını genişlet ya da lookup bağla; paralel tablo üretme |
| "işe alım, kadro talebi, ilan, aday / CV, mülakat, teklif" | İK › Recruitment | Var olan `Hr_T_StaffRequest` / `Hr_T_JobPosting` / `Hr_T_Candidate` / `Hr_T_JobApplication` / `Hr_T_Interview` / `Hr_T_JobOffer` ekranını genişlet; personel kullanıcısını BT elle açar, aday kaydına `UserId` ile bağlanır |
| "performans değerlendirme, hedef / KPI / OKR, yetkinlik değerlendirmesi, 360, kalibrasyon, gelişim planı" | İK › Performance Management | Var olan `Hr_T_Performance*` / `Hr_T_DevelopmentPlan` ekranını genişlet; yeni persona ekranı aynı tabloya `DefaultFilter` + `Hr_S_PerformanceGuard` ile açılır (`configs/seeds/docs/modules/App.Hr.md` › Performans Yönetimi) |
| "izin talebi, izin onayı, izin bakiyesi, yıllık izin hakkı, rapor/istirahat izni" | İK › Leave Management | Var olan `Hr_T_LeaveRequest` ekranını genişlet; persona ekranı aynı tabloya `DefaultFilter` + `Hr_S_LeaveRequestGuard` ile açılır, bakiye `Hr_V_LeaveSummary`'den okunur (`configs/seeds/docs/modules/App.Hr.md` › İzin Yönetimi) |
| "puantaj, devam takibi, PDKS, giriş-çıkış, geç kalma, fazla mesai, devamsızlık" | İK › Timesheet & Attendance | Var olan `Hr_T_AttendanceLog` / `Hr_T_Timesheet` ekranlarını genişlet; hesap `Hr_S_TimesheetCalculate`'te tek yerdedir, kural değişikliği oraya yazılır (`configs/seeds/docs/modules/App.Hr.md` › Puantaj ve Devam Takibi) |
| "yapay zekâ cevaplasın" | Saas › Definitions › AiBot | Bot tanımı; `/admin/ai` bu tanımı kullanır |
### 12.7 Canlı referans uygulamalar
İstenen davranışın bu uygulamada çalışan örneği. Tanımlar
`api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_{Saas,Administration,Hr}.cs`
içinde ilgili `#region` altındadır.
| İhtiyaç | Bak |
| --- | --- |
| Ağaç (TreeList) | `Menu`, `Permission`, `Department`, `JobPosition` |
| Kanban / Todo | `DevRequests` (`TodoOptionJson` + `SortOrder`, `Priority`, `Tags`) |
| Takvim (Scheduler) | `Agenda` (`SchedulerOptionJson` + `RecurrenceRule`) |
| Tek kayıtlık form ekranı | `Licence` (`ListFormType = Form` + alt kalem listesi) |
| Onay akışı | `Sales Orders` — `WorkflowJson` (`ApprovalUserFieldName`, `ApprovalStatusFieldName`, `ApprovalDateFieldName`, `ApprovalDescriptionFieldName`) + `ListFormWorkflow` düğümleri (Start → Approval → End/End) |
| Alt form (master-detail) | `Tenant → Branches`, `Sales Orders → Sales Order Item`, `Survey → Question → Option` |
| KPI widget | Saas, Administration ve İK'daki yönetim ekranlarının çoğu (Users, Tenants, Dynamic Services, Custom Components, Audit Logs, Bildirimler, Duyuru/Anket/Etkinlik, Forum…; basit tanım listeleri hariç) — `WidgetsJson`; tek sorgu `UNION ALL` ile birden çok kart döndürür (`Title`, `Value`, `Color`, `SubTitle`, `Icon`) |
| Toolbar'dan diyalog açma | `Tenant` (`TenantsConnectionString`), `Role` (`RolesPermission`), `User` (`UsersPermission`), `Audit Logs` (`AuditLogDetail`), `Notification Rule` (`CreateNotification`), `Branches` (`BranchSeed`), `Sales Orders` (`CreateTenantFromOrderDialog`), `Dynamic Services` (`DynamicServiceEditor` — hem toolbar hem satır) |
| Yazmayı servise devretme | `Tenant`, `Role`, `User` — `*ServiceAddress = "list-form-dynamic-api/{tenant\|role\|user}-{insert\|update}"`; `Notification` — `notification-insert` (çoklu değer `MultiValueStringJsonConverter`) |
| Ekle düğmesini diyaloğa bağlama | `Dynamic Services` — `EditingOptionJson.AddDialogName = "DynamicServiceEditor"` |
| Satır butonundan servis silme | `Dynamic Services` — `UiEvalService.ApiDeleteDynamicService` |
| Toplam satırı | `Sales Order Item`, `Licence Item` (`TotalSummaryJson`) |
| View üzerinden okuma | `Background Worker Jobs`, `Licence Item` (`SelectCommandType = View`) |
| Cascade lookup | `City` → `District` (ülke/il/ilçe zinciri); `Job Position` (şube → departman → üst görev) |
| Alan varsayılanı ve soft delete | `Tenant` (`FormFieldsDefaultValueJson`, `DeleteFieldsDefaultValueJson`) |
### 12.8 Uyarılar
- **Bu ekranların seeder'ı idempotenttir:** `ListFormCode` zaten varsa kayıt **güncellenmez**.
Hazır bir ekranın davranışını değiştirmek gerekiyorsa seeder'ı düzenlemek yetmez; değişikliği
`sql/execute/{Ad}.sql` altında var olan kaydı güncelleyen bir prosedürle taşı.
- **Host tablosuna kiracı verisi yazma.** `Sas_H_*` tabloları bütün kiracılar için ortaktır;
kiracıya özel veri `*_T_*` tablosuna gider. Ekranın `IsTenant`/`IsBranch` bayrakları ile
tablonun kapsam harfi tutarlı olmalıdır.
- **Yetki adı menüden gelir.** Ekranın `PermissionJson`'ındaki adlarla menünün
`RequiredPermissionName` değeri aynı sözleşmeyi göstermelidir; farklıysa kullanıcı menüyü görür,
ekranı göremez.
- **Platformun hazır tablolarına doğrudan yazan ekran üretme.** `AbpUsers`, `AbpRoles`,
`AbpTenants` yazma işi `list-form-dynamic-api` uçlarındadır; ham SQL ile yazmak parola
hash'ini, yetki önbelleğini ve kiracı kurulumunu atlar.
---
## 13. Bu dosyanın kapsamadıkları
| Konu | Nerede |
| --- | --- |
| Karar sırası, üretim protokolü, seed sözleşmesi | `lowcode.instructions.md` §0–§2 |
| Wizard seed dosyasının alanları | `lowcode.instructions.md` §4 |
| `EditorScript` tarifleri ve runtime API | `lowcode.instructions.md` §5 |
| `EditorOptions` sözlüğü ve preset'ler | `lowcode.instructions.md` §6 |
| Visual Designer doküman şeması ve event script'leri | `lowcode.instructions.md` §7 |
| SQL nesneleri, CRUD/Custom Endpoint, Dynamic Service | `lowcode.instructions.md` §8–§9 |
| Sequence, zamanlanmış iş, bildirim, rapor ekranları | `lowcode.instructions.md` §9.9–§9.14 |
| `api/` kod standardı | `dotnet.instructions.md` |
DevExtreme'in burada listelenmeyen bir seçeneğini kullanman gerekiyorsa, **nereye yazdığın
belirleyicidir**:
| Nereye | Bilinmeyen anahtara ne olur |
| --- | --- |
| Ekran seviyesindeki `*Json` kolonları (§2–§8) | **Düşer.** `GridOptionsDto` ham metni `[JsonIgnore]` ile gizler, istemciye yalnızca tipli DTO gider. DTO'ya alan eklemek Core'dur — yalnızca "core sisteme ekle" komutuyla, sonra bu dosya güncellenir |
| Alan seviyesindeki `ListFormField.EditorOptions` (§9.8) | **Geçer.** Ham metin olarak istemciye iner ve editöre olduğu gibi verilir; sözlükte olmayan anahtar Options Builder'ın "Sözlük dışı ayarlar" bölümünden de yazılabilir (`lowcode` §6.1) |
| `CustomJsSourcesJson` / `CustomStyleSourcesJson` (§5.5) | Geçer — ama son çaredir, denetlenmez |
Yani "DevExtreme'de var, platformda yok" durumunda önce §9.8'e (editör seçeneği mi?) bak; ekran
seviyesindeki bir seçenekse DTO'ya eklenmeden çalışmaz: önce envanterdeki karşılığını kur, yoksa
"Core gerektiren kısım" notuyla bildir (`lowcode` §0.8).