sozsoft-platform/.github/instructions/lowcode-reference.instructions.md
2026-09-25 10:52:06 +03:00

1499 lines
113 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 ve Administration 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/host/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**; kalıcı olarak
gerekiyorsa önce ilgili DTO'ya alan eklenir (ve bu dosya 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 menüsünde zaten var mı? | §12.5 |
| 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** | Toolbar ve satır butonları: adres, diyalog, script, yetki, koşullu görünürlük | `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 rapor şablonu | `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.5'e 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; `SelectCommand` bir sorgu metni ya da fonksiyon çağrısıysa buraya kısa bir ad yaz |
| `KeyFieldName` · `KeyFieldDbSourceType` | — · `16` String | Anahtar kolon ve `System.Data.DbType` karşılığı |
| `SelectFieldsDefaultValueJson` | — | **Okuma** sorgusuna parametre olarak katılan değerler (`FieldsDefaultValueDto[]`) — `@USERID`, `@TENANTID` gibi |
| `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` | — | Sayfa başına kayıt; `0` = tümü |
| `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 |
| `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` | Aynı ekranın dil/kullanıcı/rol varyantı | §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 |
| `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 |
| `DisplayMode` | `"adaptive"` | `adaptive` \| `compact` \| `full` |
| `ScrollingMode` | `"standard"` | `standard` \| `virtual` \| `infinite` — uzun listelerde `virtual` |
| `LoadPanelEnabled` | `"auto"` | **Metin** alanıdır: `"auto"` \| `"true"` \| `"false"` |
| `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.
### 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.
### 2.5 `StateStoringJson` (`StateStoringDto`)
| Anahtar | Varsayılan | Anlam |
| --- | --- | --- |
| `Enabled` | `true` | Kullanıcının kolon/filtre/sıralama tercihleri saklansın mı |
| `Type` | `"custom"` | `custom` (sunucuda kullanıcı bazlı) \| `localStorage` \| `sessionStorage` |
| `StorageKey` | — | Anahtar; boşsa sunucu `listFormGridStorage-{ListFormCode}` üretir |
| `SavingTimeout` | `5000` | Değişiklikten kaç ms sonra yazılacağı — **`[Range(1500, 5000)]`**, dışına çıkan değer doğrulamada reddedilir |
`Type: "custom"` tercih edilir: tercih sunucuda `ListFormCustomization` kaydı olarak
(`ListFormCustomizationTypeEnum.GridState = 2`) kullanıcı bazlı saklanır, bu yüzden başka
tarayıcıdan girildiğinde de korunur. `localStorage`/`sessionStorage` yalnızca o tarayıcıdadı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.
### 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).
**`HeaderFilterJson`** (`GridHeaderFilterDto`): `Visible` (`false`), `AllowSearch` (`false`),
`Height` (`325`), `Width` (`252`), `SearchTimeout` (`500`).
**`FilterPanelJson`** (`FilterPanelDto`): `Visible` (`false`), `FilterEnabled` (`false`),
`Texts.ClearFilter` (`"Clear"`) / `Texts.CreateFilter` (`"Create Filter"`) /
`Texts.FilterEnabledHint` (`"Enable the filter"`), `CustomizeText` (serbest JSON; filtre
metnini biçimlendirmek için — sözlükte tipi yoktur, olduğu gibi bileşene geçer).
**`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`) |
| `AllowAllDeleting` | `false` | Toplu silme düğmesi |
| `AllowDuplicate` | `false` | Kaydı kopyalayarak yeni kayıt |
| `AllowDetail` | `false` | Satırda Detay düğmesi |
| `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` | — | Aksiyon sütununun başlığı |
| `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`).
### 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` |
| `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, @USERID`).
### 4.2 Komut içinde kullanılabilen anahtarlar
| Token | Değer |
| --- | --- |
| `@ID` | Kaydın anahtarı (Delete'te seçili anahtar kümesi) |
| `@USERID` · `@USERNAME` · `@ROLES` | Oturumdaki kullanıcı |
| `@FieldName` | Ekrandan gelen o alanın değeri — tablodaki her kolon için geçerlidir (`@OrderNo`, `@Amount`) |
`*FieldsDefaultValueJson` ile üretilen değerler (`@NOW`, `@NEWID`, `@TENANTID`, Sequence…)
komuta parametre olarak katılır (`lowcode` §4.3.5). **String birleştirme ile sorgu kurma.**
### 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.
**Ç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ı |
| `I` | bool | Alan içe aktarımda kabul edilsin mi |
```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.
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` |
| `TabTitle` | Dil anahtarı |
| `Code` | Hedef `ListFormCode` |
| `IsRefresh` | Ana kayıt değişince yenilensin |
| `Relation[]` | `ParentFieldName` → `ChildFieldName` + `DbType` (`System.Data.DbType`, **sayı**; bileşik anahtarda birden çok satır) |
Alt formun kendisi de bir ListForm'dur: `IsSubForm = true` olmalı ve genellikle `CreateMenu:
false` ile menüsüz üretilir (`lowcode` §4.2). `TabType`, hedef ekranın `LayoutJson`'ında açık
olan bir görünüm olmalıdır; kapalı bir görünüm sekmede boş çizilir.
### 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. 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) |
| `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**.
- `@USERID` / `@TENANTID` gibi token'lar **çözülmez** — bunlar `*FieldsDefaultValueJson` ve
`DefaultFilter` yolunun sözleşmesidir. Widget sorgusuna yazılırsa bağlanmamış parametre
hatası verir. Kiracı kırılımı gerekiyorsa ya kiracıya özel bir `DataSource` kullan, ya da
kırılımı sorgunun okuduğu view'ın içine göm.
- Soft delete'li tabloda `"IsDeleted" = 'false'` koşulunu sorguya kendin yaz.
### 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.
### 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')"]
```
**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` | Ekran alt form olarak da kullanılıyor mu |
| `SubFormsListFormType` | `"Form"` | Alt form olarak açıldığında hangi görünümle geleceği |
| `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.6). `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
Aynı `ListFormCode` ile ikinci bir `ListForm` kaydı açılıp `CultureName`, `UserId` ya da `RoleId`
doldurulursa o dil/kullanıcı/rol için **farklı sütun düzeni ve ayarlar** verilebilir. Aynı şey
`ListFormField` ve `Menu` kayıtları için de geçerlidir. Wizard bunu üretmez; ihtiyaç varsa
ListForm editöründen açılır ve seed karşılığı elle yazılır.
---
## 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"` |
### 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` | `none` | `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.
---
## 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`)
Ağaç alanlarının tamamı + `TitleExpr`*, `StartExpr`*, `EndExpr`*, `ProgressExpr`*
(istemci dördü olmadan görünümü açmaz — `List.tsx` `isLayoutValid`), `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`).
### 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ığı |
| `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. Gerekirse önce DTO'ya
> geriye dönük uyumlu alan eklenir.
### 7.4 `TodoOptionJson` (`TodoOptionDto`) — Kanban
`TitleExpr`*, `StatusExpr`*, `DescriptionExpr`, `DueDateExpr`, `TagExpr`, `AssigneeExpr`,
`PriorityExpr`, `CompletedExpr`, `OrderExpr`, `TimesheetExpr`, `SubTaskExpr`,
`StatusOrder` (kolon sırası, virgüllü), `AllowDragging` (varsayılan `true`).
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 |
Kullanıcı/rol bazlı farklı grafik, ekranın `UserId`/`RoleId` varyantıyla kurulur (§5.7); boşsa
grafik herkese aynıdır.
### 8.1 `SeriesJson` (`ChartSeriesDto[]`)
| Anahtar | Varsayılan | Anlam |
| --- | --- | --- |
| `Name` | — | Açıklamada görünen seri adı |
| `Type` | — (DevExtreme: `line`) | `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"` | `sum` \| `avg` \| `count` \| `min` \| `max` — sunucuda `ArgumentField` kırılımıyla toplanır (§8.2) |
| `Color` | — | Seri rengi |
| `Axis` · `Pane` | — | Hangi eksen/bölme |
| `Visible` | `true` | |
| `ShowInLegend` | `true` | |
| `Width` | `2` | Çizgi kalınlığı |
| `DashStyle` | `"solid"` | `solid` \| `dash` \| `dot` \| `longDash` |
| `BarWidth` · `BarPadding` · `BarOverlapGroup` · `CornerRadius` | — | Çubuk serileri |
| `IgnoreEmptyPoints` | `false` | |
| `SelectionMode` | `"none"` | `onlyPoint` \| `allSeriesPoints` \| `allArgumentPoints` \| `includePoints` \| `excludePoints` \| `none` |
| `RangeValue1Field` / `RangeValue2Field` | `val1` / `val2` | Aralık serileri |
| `Label` | — | `ChartLabelDto`: `Visible`, `Format`, `Font`, `BackgroundColor`, `CustomizeText` |
| `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
Grafik ayrı bir sorgu çalıştırmaz: 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.
`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` (`hide` \| `stagger` \| `none`). 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.**
### 8.6 `LegendJson` (`ChartLegendDto`)
`Visible` (`true`), `Position` (`outside` \| `inside`), `Orientation` (`vertical` \| `horizontal`),
`ColumnCount`, `RowCount`, `Title`, `BackgroundColor`, `Border`.
### 8.7 `TooltipJson` (`ChartTooltipDto`)
`Enabled`, `Shared`, `Format` / `ArgumentFormat` (DevExtreme format adları),
`Color` (`#ffffff`), `ArrowLength` (`10`), `Location` (`center` \| `edge`),
`PaddingLeftRight` (`18`), `PaddingTopBottom` (`15`), `Border`, `Font`.
### 8.8 Diğerleri
- `PanesJson` (`ChartPanesDto[]`): `Name`, `Height` (`250`), `BackgroundColor`.
Seriler `Pane` alanıyla bir bölmeye bağlanır.
- `CrosshairJson` (`ChartCrosshairDto`): `Enabled`, `Color` (`#f05b41`), `Width` (`1`),
`DashStyle` (`dot`), `HorizontalLine`, `VerticalLine`, `Label`.
- `ScrollBarJson` (`ChartScrollBarDto`): `Visible`, `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`.
- `AnnotationsJson` (`ChartAnnotationDto[]`): `Name`, `Type` (`text` \| `image`), `Text`, `Image`,
konum için `Series` + `Argument`/`Value` ya da piksel `X`/`Y`, `OffsetX`/`OffsetY` (`-1`),
`Width`/`Height`, `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`, `Size` `12`, `Weight` `400`),
`Border` (`Visible`, `Color` `#d3d3d3`, `Width` `1`, `CornerRadius` `0`, `DashStyle` `solid`).
---
## 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[]` | İzin verilen operatörler; boş bırakılırsa tipe göre otomatik. Değerler: `=`, `<>`, `<`, `<=`, `>`, `>=`, `contains`, `notcontains`, `startswith`, `endswith`, `isblank`, `isnotblank`, `between`, `anyof`, `noneof` |
| `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). 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.
### 9.4 `ColumnCustomizationJson` (`ColumnCustomizationDto`)
`Fixed` (`false`), `FixedPosition` (`left` \| `right`), `AllowReordering` (`false`).
Sol/sağa sabitlenmiş kolon yatay kaydırmada sabit kalır.
### 9.5 `TotalSummaryJson` / `GroupSummaryJson` (`ColumnTotalSummaryDto`)
| Anahtar | Anlam |
| --- | --- |
| `SummaryType` | `sum` \| `avg` \| `count` \| `min` \| `max` (`SummaryTypeEnum`; `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` | `false` — grup alt bilgisinde göster (yalnızca `GroupSummaryJson`) |
| `AlignByColumn` | `false` — grup satırında ilgili sütunun altına hizala; `false` iken özet grup başlığının yanında parantez içinde çı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.
"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` |
| `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ı |
| `JoinCondition2` | İkinci koşulun bağlacı (`AND` varsayılan \| `OR`) |
| `JoinFieldName2` | İkinci koşulun alanı — **tablo adıyla birlikte** (`Def_T_City.CultureName`) |
| `BaseTableJoinFieldName2_OrValue` | İkinci koşulun karşı tarafı: tablo adlı bir alan ya da `'en'` gibi sabit |
Üç-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 |
| `CaptionName` | — | Sütun başlığı — **dil anahtarı** |
| `PlaceHolder` | — | Editörün placeholder metni |
| `Alignment` | `"left"` | `left` \| `center` \| `right` |
| `Format` | — | Hücre biçimi (DevExtreme format adı ya da desen) |
| `BandName` | — | Sütunu bir başlık bandının altına toplar |
| `Width` · `ListOrderNo` | — | Genişlik ve grid'deki sıra |
| `Visible` | — | Sütun grid'de görünür mü; `false` ise kolon seçicide durur ama **sorguya girer** |
| `IsActive` | `true` | `false` olan alan **sorguya bile girmez** |
| `SortIndex` · `SortDirection` | — | Açılıştaki sıralama (`asc` \| `desc`); çok sütunlu sıralamada `SortIndex` sırası belirler |
| `AllowSearch` · `AllowEditing` · `AllowAdding` | `true` | Arama paneline dâhil olma, güncellemede ve yeni kayıtta düzenlenebilirlik |
| `ColumnCssClass` · `ColumnCssValue` | — | Koşulsuz stil (sınıf adı + sınıfın içeriği); koşullu olan `ColumnStylingJson` |
| `SourceDbType` | `16` String | Kolonun `System.Data.DbType` karşılığı |
| `EditorType2` · `EditorOptions` · `EditorScript` | — | Editör tipi ve davranışı (`lowcode` §5, §6) |
| `ColSpan` · `EditOrderNo` · `EditGroupOrderNo` | — | Düzenleme formundaki yeri; `EditGroupOrderNo` `EditingFormJson`'daki `Order` ile eşleşir |
| `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/Form/SelectFieldsDefaultValueJson`
> 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) |
| 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 = var olan bileşene ekle, yeni bileşen yazma:** İstenen komponent bu
> envanterle büyük ölçüde kuruluyor ama bir bileşende **olay, prop ya da küçük bir yetenek**
> eksikse komponent yine üretilir: eksik 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 **yapılmaz** —
> bunlar çerçeve dışıdır ve onay ister (`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. `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 ve Administration menüleri — platform yetenek kataloğu
Bu iki 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.6).
### 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). 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), `Adm_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;
`Adm_T_Sequence` Saas menüsündedir. Ö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 › 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. |
| **Definitions › Skill Type** | `App.Definitions.SkillType` (alt formlar `.Skill`, `.SkillLevel`) | `Sas_H_SkillType` · `Sas_H_Skill` · `Sas_H_SkillLevel` | host | Yetkinlik tanımı; bir ana ekrandan iki alt forma açılan **çoklu alt form** örneği. |
| **Definitions › UoM Category** | `App.Definitions.UomCategory` (alt form `.Uom`) | `Sas_H_UomCategory` · `Sas_H_Uom` | host | Ölçü birimi, `Ratio`/`Rounding` — miktar alanı olan ekranlarda birim lookup'ı ve çevrim. |
| **Definitions › Sequence** | `App.Definitions.Sequence` | `Adm_T_Sequence` | tenant | Belge numarası üretimi (`lowcode` §9.9); alan varsayılanında `CustomValueType: 5` ile bağlanır. |
| **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.6). |
| **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. |
| **Dev Requests** | `App.DevRequests` | `Sas_H_DevRequest` | host | **Kanban/Todo düzeninin** referansı (`DefaultLayout: todo`): `Status`, `Priority`, `SortOrder`, `Tags`, `Assignees`, `SubTasks`, `Timesheets`. MCP geliştirme döngüsünün kuyruğudur (`Ready to Start` → `Testing`, `Staging`'e kullanıcı alır; araç çubuğunda DB Migrate butonu vardır — `UiEvalService.ApiDbMigrate()`, `App.Setup.Migrate`; yalnızca `start_dev_request` başlatır; açıklamadaki base64 görseller MCP'ye resim içeriği olarak gider, not `append_dev_request_note` ile eklenir; `lowcode.instructions.md` §0.11). |
| **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. |
| **Setting Definitions** | `App.SettingDefinitions` | `Sas_H_SettingDefinition` | host | Yeni uygulama ayarı: `Code`, `MainGroupKey`/`SubGroupKey`, `DataType`, `SelectOptions`, `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`. **İ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 › Module / Routes / Menu / Manager** | `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. 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 › 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/{kapsam}/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 menüsü
> `App.Definitions.OrgChart` ister ama `/admin/organization` rotası `App.Definitions.Department`
> ister (menüyü gören ama sayfayı açamayan kullanıcı olabilir). 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 › Department** | `App.Definitions.Department` | `Adm_T_Department` | `ParentId` ile **ağaç (TreeList) düzeninin** referansı; personel/onay ekranlarının departman lookup'ı. |
| **Definitions › Job Position** | `App.Definitions.JobPosition` | `Adm_T_JobPosition` | Ünvan ağacı (`ParentId` + `DepartmentId`) — departmana bağlı cascade örneği. |
| **Definitions › Org Chart** | menü yetkisi `App.Definitions.OrgChart` · `/admin/organization` | `Adm_T_Department` · `Adm_T_JobPosition` | Departman/ünvan verisinin organizasyon şeması görünümü (özel React sayfası). |
| **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. |
| **Intranet › Announcement** | `App.Intranet.Announcement` (alt formlar Like/Comment) | `Adm_T_Announcement` · `Adm_T_AnnouncementLike` · `Adm_T_AnnouncementComment` | Duyuru + beğeni + yorum: **ana kayıt / alt etkileşim** kalıbının hazır örneği. |
| **Intranet › Survey** | `App.Intranet.Survey` (alt form Question → Option; Response → Answer) | `Adm_T_Survey` · `Adm_T_SurveyQuestion` · `Adm_T_SurveyQuestionOption` · `Adm_T_SurveyResponse` · `Adm_T_SurveyAnswer` | Anket motoru; soru–seçenek–yanıt zinciri **iki seviyeli alt form** ihtiyacının referansıdır. |
| **Intranet › Social Post** | `App.Intranet.SocialPost` | `Adm_T_SocialPost` · `Adm_T_SocialComment` · `Adm_T_SocialLike` (+ `SocialPollOption`, `SocialMedia`, `SocialLocation`) | Kurum içi akış; `IsLiked`, `IsOwnPost` gibi hesaplanmış kolonların view içinde üretilmesine örnek. Alt formlar: `App.Intranet.SocialMedia`, `SocialComment`, `SocialLike` (`Id` → `SocialPostId`); anket seçenekleri (`App.Intranet.SocialPollOption`) medyaya bağlı olduğu için `SocialMedia` ekranının alt formudur (`Id` → `SocialMediaId`). |
| **Intranet › Event Type / Category / Event** | `App.Intranet.Events.EventType` · `.EventCategory` · `.Event` | `Adm_T_EventType` · `Adm_T_EventCategory` · `Adm_T_Event` · `Adm_T_EventPhoto` · `Adm_T_EventLike` · `Adm_T_EventComment` | 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.Intranet.Events.Event` altındadır. `Adm_T_EventPhoto` tablosu `sql/object/Adm_T_EventPhoto.sql` ile oluşturulur. |
| **Intranet › Documents** | `App.Intranet.Document` · `/admin/intranet-documents` | Dosya yöneticisi (`intranet` / `intranet\|_passive` klasörleri) | 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. |
| **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, **KPI widget'ları** ve `list-form-dynamic-api/user-*` ile devredilen yazma. Kullanıcı `WorkHour`, `DepartmentId`, `JobPositionId` alanlarını taşı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); ekrana `CommandColumnJson` butonuyla bağlanır (`lowcode` §9.12). |
| **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,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 ListForm olmayan (özel React) ekranlar
Bu ekranlar `Sas_H_Route` kayıtlarıyla yönetilir (seed:
`configs/seeds/host/data/App.Menus.Routes.json`). Yenisini eklemek **kod yazmak demektir** — karar
sırasının son adımıdır ve çerçeve dışıdır (onay ister, `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.Intranet.Document` |
| `/admin/ous` · `/admin/organization` | `organization-unit/OrganizationUnits` · `hr/OrgChart` | `Abp.Identity.OrganizationUnits` · `App.Definitions.Department` (menü `App.Definitions.OrgChart` ister — §12.2 notu) |
| `/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.5 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" | Saas › 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 basılsın" | Administration › Reports | Hazır `DynamicGrid` ya da `ReportTemplate` |
| "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 |
| "ş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, departman, ünvan" | Saas / Administration › Definitions | Var olan tabloya lookup bağla, yenisini üretme |
| "yapay zekâ cevaplasın" | Saas › Definitions › AiBot | Bot tanımı; `/admin/ai` bu tanımı kullanır |
### 12.6 Canlı referans uygulamalar
İstenen davranışın bu uygulamada çalışan örneği. Tanımlar
`api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_{Saas,Administration}.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 | `User`, `Dynamic Services`, `Custom Components` — `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) |
| Alan varsayılanı ve soft delete | `Tenant` (`FormFieldsDefaultValueJson`, `DeleteFieldsDefaultValueJson`) |
### 12.7 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. Kalıcı olarak gerekiyorsa önce ilgili DTO'ya alan eklenir, 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ışmayacağını bilerek planla.