diff --git a/.github/instructions/ai.instructions.md b/.github/instructions/ai.instructions.md index 851f2738..02c60b8d 100644 --- a/.github/instructions/ai.instructions.md +++ b/.github/instructions/ai.instructions.md @@ -232,12 +232,9 @@ Supported component families: Runtime UI capabilities: - Per-list layout switching: - - Grid - - Pivot - - Tree - - Chart - - Gantt - - Scheduler + - Grid, Card, Pivot, Chart — on by default for every generated list screen; turn one off only + when the request explicitly excludes it (`lowcode.instructions.md` §9.8) + - Tree, Gantt, Scheduler, Todo — off by default; opened on request with their required `*Expr` fields - Per-user layout persistence - Lazy loading and view preloading - Shared dynamic datasource pipeline across layouts diff --git a/.github/instructions/lowcode-reference.instructions.md b/.github/instructions/lowcode-reference.instructions.md index 3501a23d..9a6662b2 100644 --- a/.github/instructions/lowcode-reference.instructions.md +++ b/.github/instructions/lowcode-reference.instructions.md @@ -10,8 +10,10 @@ gelen bütün ekranları** ve bunların yeni bir komponentte nasıl kullanılaca 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/**` ve -`api/src/Sozsoft.Platform.Domain/Entities/Tenant/ListForm/ListForm.cs` dosyalarından çıkarılmıştır. +**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** @@ -23,11 +25,16 @@ sorusunda buraya bak. `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` bu 47 - kolonun ham metnini `[JsonIgnore]` ile gizler ve istemciye yalnızca tipli DTO'ya - (`SchedulerOptionJson` → `SchedulerOptionDto`) çözülmüş hâlini yollar. Dolayısıyla: +- **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. @@ -51,7 +58,7 @@ ama hiçbirini görmeden bırakma; kullanıcı "şunu da yapabilir miydik?" dedi | 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 | `lowcode` §6 | +| 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 | @@ -65,7 +72,7 @@ ama hiçbirini görmeden bırakma; kullanıcı "şunu da yapabilir miydik?" dedi | 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** | `IsTenant`, `IsBranch`, `IsOrganizationUnit`, sorgularda kırılım | `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 | @@ -100,10 +107,11 @@ ve çoğu isteğin cevabı aslında oradadır: | `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` | — | `UiGridSortModeEnum`: `1` Single · `2` Multiple · `3` None | +| `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` · `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 | | --- | --- | --- | @@ -127,10 +135,8 @@ ve çoğu isteğin cevabı aslında oradadır: | `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` ve diğer 21 grafik kolonu | Chart görünümü | §8 | +| `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 | -| `AdaptiveLayoutJson` | Dar ekranda öğe gizleme (grafik) | §8.4 | -| `CommonJson` · `DataSourceJson` | Grafik geneli ve grafik sorgusu | §8.2 · §8.3 | | `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 | @@ -206,12 +212,17 @@ Toolbar butonları seçili satırlarla çalışır (`lowcode` §4.9); onay akı | --- | --- | --- | | `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 ekran kodundan türer | +| `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 @@ -255,7 +266,7 @@ metnini biçimlendirmek için — sözlükte tipi yoktur, olduğu gibi bileşene | `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 (JSON nesnesi). Toolbar'da satır bağlamı olmadığı için `@Kolon` token'i kullanılmaz | +| `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`), @@ -384,7 +395,7 @@ Ekranın üstündeki KPI şeridi. **Sözleşmesi sezgisel değildir, dikkatle ok | `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` | `false` ise sorgu hiç çalıştırılmaz | +| `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: @@ -400,26 +411,31 @@ SELECT 'Onaylı', COUNT(*), 'green', 'FaCheck' FROM Mrp_T_Order WHERE Status = 2 { "Title": "Baslik", "Value": "Deger", "Color": "Renk", "Icon": "Ikon", "ColSpan": 4, "IsActive": true } ``` -Sorgu ekranın `DataSourceCode` bağlantısında, **olduğu gibi** çalıştırılır: +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` · `Tree` · `Gantt` · `Scheduler` | `true` | -| `Todo` | `false` | +| `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. **DTO varsayılanları yanıltıcıdır:** blok -yazılmazsa yedi görünüm birden açık gelir; ekranın üstünde çalışmayan görünüm düğmeleri -çıkmasın diye wizard bu bloğu her zaman açıkça yazar. `DefaultLayout`, `true` olan bir görünümü -göstermelidir (§7'deki zorunlu `*Expr` alanları boşsa o görünümün JSON'u hiç yazılmaz). +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` @@ -491,16 +507,19 @@ Pivot ekranı ayrı bir sorgu istemez; ızgarayla aynı `SelectCommand` üzerind ### 7.1 `TreeOptionJson` (`TreeOptionDto`) -`KeyExpr`*, `ParentIdExpr`*, `HasItemsExpr`, `RootValue` (varsayılan `null`), -`ExpandedRowKeys[]`, `AutoExpandAll` (`false`), `RecursiveSelection` (`false`), -`TitleExpr`, `StartExpr`, `EndExpr`, `ProgressExpr`. +`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`, -`ScaleType` (varsayılan `weeks`; tam küme `GanttScaleTypeEnum`: `auto` \| `minutes` \| `hours` \| -`sixHours` \| `days` \| `weeks` \| `months` \| `quarters` \| `years`) ve izin bayrakları: -`AllowEditing`, `AllowTaskAdding`, `AllowTaskUpdating`, `AllowTaskResourceUpdating`, +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`). @@ -509,7 +528,7 @@ Ağaç alanlarının tamamı + `TitleExpr`*, `StartExpr`*, `EndExpr`*, `Progress | Anahtar | Varsayılan | Anlam | | --- | --- | --- | | `TextExpr`* · `StartDateExpr`* · `EndDateExpr`* | — | Başlık ve zaman kolonları | -| `UserNameExpr` · `DescriptionExpr` | — | Kaynak/kişi ve açıklama | +| `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ığı | @@ -519,11 +538,12 @@ Ağaç alanlarının tamamı + `TitleExpr`*, `StartExpr`*, `EndExpr`*, `Progress | `FirstDayOfWeek` | `1` | 0 = Pazar | | `CrossScrollingEnabled` | `false` | Kaynak (resource) görünümünde yatay+dikey kaydırma | | `AllowAdding` / `AllowEditing` / `AllowDeleting` / `AllowDragging` / `AllowResizing` | `false` | | -| `Resources[]` | — | Randevuları renklendiren kaynak eşlemesi (`FieldExpr`, `DataSource[]`, `Label`, `UseColorAsDefault`) | -`Resources` C# `SchedulerOptionDto`'da **tanımlı değildir**; yalnızca UI modelinde (`proxy/form/ -models.ts`) vardır. Yani seed'den yazılabilir (bilinmeyen anahtar korunur ve bileşene geçer) ama -ListForm editöründeki tipli formdan düzenlenemez; kalıcı kullanılacaksa C# DTO'suna eklenmelidir. +> **`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 @@ -540,9 +560,10 @@ bırakıldığında pano sayfanın kalan yüksekliğini kaplar ve her kolon kend ## 8. Chart (grafik) sözleşmesi -Grafik, ListForm üzerindeki **22 ayrı JSON kolonuyla** yapılandırılır. Hepsi DevExtreme +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). +(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. @@ -550,10 +571,9 @@ Grafik iki yoldan açılır: ekranın `LayoutJson.Chart` bayrağıyla bir görü | Kolon | Karşılığı | | --- | --- | | `SeriesJson` | `series[]` — **zorunlu**, §8.1 | -| `DataSourceJson` | Grafiğin kendi sorgusu, §8.2 | -| `CommonJson` | Grafik geneli (palet, tema), §8.3 | -| `CommonSeriesSettingsJson` | Tüm serilere ortak ayarlar | -| `ArgumentAxisJson` · `ValueAxisJson` · `CommonAxisSettingsJson` | Eksenler, §8.5 | +| `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) | @@ -566,18 +586,18 @@ Grafik iki yoldan açılır: ekranın `LayoutJson.Chart` bayrağıyla bir görü | `AnimationJson` | Animasyon | | `ExportJson` | Dışa aktarma/yazdırma | -Ayrıca grafiğe **kullanıcı/rol kısıtı**: ListForm kaydındaki `UserId` / `RoleId` alanları boşsa -grafik herkese açıktır. +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` | `line` | `line` \| `bar` \| `spline` \| `area` \| `scatter` \| `bubble` \| `stackedbar` \| `fullstackedbar` \| `stackedline` \| `stackedarea` \| `steparea` \| `stepline` \| `rangebar` \| `rangearea` \| `candlestick` \| `stock` ve stacked/fullstacked varyantları | -| `ArgumentField` | `"arg"` | X ekseni kolonu | +| `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` \| `custom` | +| `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` | | @@ -589,17 +609,19 @@ grafik herkese açıktır. | `SelectionMode` | `"none"` | `onlyPoint` \| `allSeriesPoints` \| `allArgumentPoints` \| `includePoints` \| `excludePoints` \| `none` | | `RangeValue1Field` / `RangeValue2Field` | `val1` / `val2` | Aralık serileri | | `Label` | — | `ChartLabelDto`: `Visible`, `Format`, `Font`, `BackgroundColor`, `CustomizeText` | -| `UserId` | — | Seriyi yalnızca bu kullanıcıya göster | +| `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 `DataSourceJson` (`ChartDataSourceDto`) +### 8.2 Veri — grafik ekranın kendi sorgusundan beslenir -```json -{ "Query": "SELECT Status AS arg, SUM(Amount) AS val FROM Mrp_T_Order GROUP BY Status" } -``` +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. -Grafik, ızgaranın `SelectCommand`'ından **ayrı** bir sorgu kullanabilir. Sorgu kolonları -`ArgumentField` / `ValueField` adlarıyla eşleşmelidir. Ekranın filtresi grafik isteğine -`Filter` parametresi olarak taşınır. +`DataSourceJson` kolonu ve `ChartDataSourceDto { Query }` / `ChartDataRequestDto` tipleri kodda +durur ama **okunmaz** — oraya sorgu yazmak grafiği değiştirmez. ### 8.3 `CommonJson` (`ChartCommonDto`) @@ -607,7 +629,7 @@ Grafik, ızgaranın `SelectCommand`'ından **ayrı** bir sorgu kullanabilir. Sor | --- | --- | --- | | `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"` | `generic.*` ve `material.*.light` varyantları | +| `Theme` | `"generic.light"` | DTO'da durur ama `Chart.tsx` grafiğe geçirmez — tema uygulamanın temasından gelir | | `ContainerBackgroundColor` | `"#FFFFFF"` | | | `AdjustOnZoom` | `true` | | | `DefaultPane` | — | | @@ -616,7 +638,7 @@ Grafik, ızgaranın `SelectCommand`'ından **ayrı** bir sorgu kullanabilir. Sor ### 8.4 Boyut ve yerleşim - `SizeJson` (`ChartSizeDto`): `UseSize` (`false`), `Width` (`400`), `Height` (`200`). - `UseSize: false` iken grafik kapsayıcıya yayılır. + `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`). @@ -629,18 +651,23 @@ Grafik, ızgaranın `SelectCommand`'ından **ayrı** bir sorgu kullanabilir. Sor ### 8.5 Eksenler -`ArgumentAxisJson` (`ChartArgumentAxisDto`): `ArgumentType` (`datetime` \| `numeric` \| `string`), -`AggregationInterval` (`day` \| `hour` \| `minute` \| `month` \| `quarter` \| `second` \| `week` \| `year`), -`Position` (`bottom` \| `left` \| `right` \| `top`), `HoverMode` (`none` \| `allArgumentPoints`), -`Grid`, `Label` → `DisplayMode` (`standard` \| `rotate` \| `stagger`), `RotationAngle` (`90`), -`OverlappingBehavior` (`hide` \| `rotate` \| `stagger` \| `none`). +`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` (`ChartValueAxisDto`): `Name`, `Title`, `ValueType` (`numeric`), -`Position` (`left`), `Type` (`continuous` \| `discrete` \| `logarithmic`), `Visible` (`true`), -`Width` (`1`), `Grid`, `Breaks[]`, `BreakStyle`, `AutoBreaksEnabled` (`true`), -`MaxAutoBreakCount` (`2`). +`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` iki eksene birden uygulanır; **eksene özel ayar varsa yok sayılır.** +`CommonAxisSettingsJson` (`ChartCommonAxisDto`: `Title`, `Visible`, `Width`, `Grid`) iki eksene +birden uygulanır; **eksene özel ayar varsa yok sayılır.** ### 8.6 `LegendJson` (`ChartLegendDto`) @@ -664,8 +691,14 @@ Grafik, ızgaranın `SelectCommand`'ından **ayrı** bir sorgu kullanabilir. Sor - `ZoomAndPanJson` (`ChartZoomAndPanDto`): `ArgumentAxis` / `ValueAxis` (`none` \| `pan` \| `zoom` \| `both`), `AllowMouseWheel` (`true`), `AllowTouchGestures` (`true`), `DragToZoom` (`false`), `PanKey` (`shift`), `DragBoxStyle`. -- `AnnotationsJson` (`ChartAnnotationDto[]`): `Name`, `Text`, `Image`, `Series`, `Argument`, - `OffsetX`/`OffsetY`, `Color`, `Font`, `Border`, `TooltipEnabled`, `Description`. +- `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`). --- @@ -685,14 +718,18 @@ dışında kalanlar: ### 9.2 `ColumnHeaderJson` (`ColumnHeaderDto`) -`AllowHeaderFiltering` (`true`), `AllowSearch` (`false`) ve **özel başlık filtresi listesi**: +`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] } ] } + "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`). @@ -707,7 +744,7 @@ Sol/sağa sabitlenmiş kolon yatay kaydırmada sabit kalır. | Anahtar | Anlam | | --- | --- | -| `SummaryType` | `sum` \| `avg` \| `count` \| `min` \| `max` \| `custom` | +| `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 | @@ -731,7 +768,7 @@ Bir kolonu **başka bir tablodan** çekmek için; view yazmadan tek alanlık joi | `JoinFieldName` | Join kurulan tablonun ilişkili alanı | | `SelectFieldName` | Ekranda gösterilecek alan | | `FieldNameAlias` | Alanın takma adı | -| `JoinCondition2` | İkinci koşulun bağlacı (`AND` \| `OR`) | +| `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 | @@ -759,7 +796,8 @@ Bir kolonu **başka bir tablodan** çekmek için; view yazmadan tek alanlık joi | `PermissionJson` | — | Alan yetkileri (§5.1) | | `CultureName` · `UserId` · `RoleId` | — | Alan varyantı (§5.7) | -> **`DefaultValue` / `DefaultValueType` / `ReadOnly` diye bir alan kolonu yoktur.** +> **`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 @@ -768,7 +806,8 @@ Bir kolonu **başka bir tablodan** çekmek için; view yazmadan tek alanlık joi ### 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: +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) | | --- | --- | --- | @@ -782,6 +821,16 @@ metni istemciye **her hâlükârda** gider; tipli DTO'lar onun yanında ek bir k `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 @@ -798,9 +847,9 @@ otomatik çıkarılan metadata (`generated/componentProps.json`). | --- | --- | | `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` | `ListView`, `DataGridView`, `TreeView`, `GanttView`, `TodoBoard`, `CardView`, `SchedulerView`, `PivotView`, `ChartView` — hepsi aynı üç prop'u alır: `listFormCode` (**zorunlu**), `height` (varsayılan `520px`, `ChartView`'da `420px`), `className` | -| `html` | `div`, `p`, `span`, `h1`, `h2`, `h3`, `h4`, `h5`, `img` — **yalnız bu dokuz etiket** | -| `ui` | `Avatar`, `Badge`, `Button`, `Calendar`, `Card`, `Checkbox`, `Chips`, `ColorPicker`, `DatePicker`, `ImageViewer`, `Input`, `Knob`, `Marquee`, `Notification`, `Progress`, `Radio`, `RangeCalendar`, `Rate`, `ScrollBar`, `Skeleton`, `Slider`, `Spacer`, `Spinner`, `Switcher`, `Tag`, `TimeInput`, `Tooltip`, `Upload` | +| `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** @@ -812,8 +861,8 @@ Kutu dışında tutulanlar (`EXCLUDED_COMPONENTS`): `Alert`, `Breadcrumb`, `Dial 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`, `Card`, `Table`, `Tabs` -ve **her custom component**. Diğer düğümlerin içine bırakma; kanvas kabul etse de üretilen kod +Ç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: @@ -954,7 +1003,7 @@ istiyorsan `Grid` (data) ya da `DataGridView` (platform) kullan. ### 10.7 `platform` bileşenleri — prop sözleşmesi -Dokuzu da aynı üç prop'u alır; başka prop'ları **yoktur** (görünümün tüm davranışı gömülen +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 | @@ -968,14 +1017,30 @@ ListForm ekranının kendi tanımından gelir): 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`, `className` (`max-w-full rounded-md`) | `onClick`, `onLoad` | +| `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). @@ -1056,8 +1121,9 @@ Script kapsamındaki her şey (`refs`, `api`, `notify`, `translate`, `checkPermi | `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 bileşen `DESIGNER_PRIMARY_EVENTS` kaydıdır — panel yalnızca bu olayları gösterir. +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 | @@ -1067,6 +1133,7 @@ Kayıtta olmayan bileşenlerde bütün `function` prop'ları listelenir; işe ya | `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` | @@ -1081,8 +1148,12 @@ bir kez çalışır; açılış verisi çekme ve `refs` üzerinden ilk durumu ku ## 11. Zamanlanmış e-posta (MailQueue modülü) -`BackgroundWorker` kaydı `WorkerType: 1` (MailQueueWorker) ise gövde bu modüldür -(`api/modules/Sozsoft.MailQueue`). +`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`) @@ -1105,15 +1176,16 @@ birden fazlası `};{` ile birleştirilir: - `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ı `BackgroundWorker_MailQueueTableFormat` +- 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ı (`BackgroundWorker_MailQueue`) +### 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ı `BackgroundWorker_MailQueueEvents` tablosunda izlenir. +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 @@ -1143,12 +1215,14 @@ Platform tablolarının adı `TableNameResolver` tarafından üretilir: | Parça | Değerler | | --- | --- | -| Modül ön eki (`MenuPrefix`) | `Sas` Saas · `Adm` Administration · `Crm` · `Crd` Coordinator · `Scp` SupplyChain · `Mnt` Maintenance · `Str` Store · `Prj` Project · `Hr` · `Mrp` · `Acc` Accounting | -| Kapsam | `H` host (tüm tenant'lar için ortak) · `T` tenant · `B` şube | +| 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). Özel view'ler +(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 @@ -1163,84 +1237,87 @@ soneklerini alır. | Ekran | Kod / route | Tablo | Kapsam | Komponent üretirken ne işe yarar | | --- | --- | --- | --- | --- | -| **Tenants** | `AbpTenantManagement.Tenants` | `AbpTenants` | host | Kiracı kaydı, bağlantı dizesi diyaloğu (`TenantsConnectionString`), alt form olarak şubeler. 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. | | **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 lookup'ı (`Title`, `Abbreviation`). | +| **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. `Country` ayrıca `PhoneCode`, `PhoneNumberFormat`, `ZipRequired`, `StateRequired`, `TaxLabel` taşır — adres doğrulaması için kullan. | -| **Definitions › Skill Type / Skill / Skill Level** | `App.Definitions.SkillType` (+ alt kayıtlar) | `Sas_H_SkillType` · `Sas_H_Skill` · `Sas_H_SkillLevel` | host | Yetkinlik ağacı; `SkillType` kendi kendine ebeveyn olan alt form örneğidir. | -| **Definitions › UoM Category / UoM** | `App.Definitions.UomCategory` | `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.instructions.md` §9.9); alan varsayılanında `CustomValueType: 5` ile bağlanır. | -| **Public › Home / About / Services / Contact** | `App.DeveloperKit.CustomComponents` — DeveloperKit > Custom Components (Visual Designer) | `data/App.DeveloperKit.CustomComponents/{Id}.json` (her bileşen kendi satır dosyasında) | host | Genel siteye açık sayfalar Custom Component'tir; `RoutePath` `/admin/` ile başlamıyorsa otomatik public route olur. Public sayfa isteği kod değil bu tasarımcıdır (README.md §10.4). | -| **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 lookup kaynağı. | -| **Public › Sales Orders** | `App.Orders.SalesOrders` | `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ı. Veri `configs/seeds/host/data/App.BlogManagement.Category.json` ve `App.BlogManagement.Posts.json` dosyalarından seed edilir. | -| **Public › Demos** | `App.Demos` | `Sas_H_Demo` | host | Siteden gelen demo talepleri — public form → tablo akışının örneği. | -| **Dev Requests** | `App.DevRequests` | `Sas_H_DevRequest` | host | **Kanban/Todo düzeninin** referansı: `Status`, `Priority`, `SortOrder`, `Tags`, `Assignees`, `SubTasks`, `Timesheets`. | -| **Setting Definitions** | `App.SettingDefinitions` | `Sas_H_SettingDefinition` | host | Yeni uygulama ayarı: `Code`, `MainGroupKey`/`SubGroupKey`, `DataType`, `SelectOptions`, `DefaultValue`, `Providers`, `RequiredPermissionName`, `IsEncrypted`, `IsVisibleToClients`. Ayarlanabilir olması gereken değeri koda gömme. | +| **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`. | +| **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 (ve `LanguagesData.json`'a) yazılır. | -| **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). `IsTenant` ekran önce tenant kaydını, yoksa `DataSourceCode`'u kullanır — çö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`). `Scope` alanı işin gövdesinin hangi bağlamda çalışacağını belirler: `Host` / `Tenant` / `Host ve Kiracılar`; her kapsam ayrı bir iş kaydıdır, `Tenant` kolonu hangi kiracıda çalıştığını gösterir. Ayrıntı: §9.10, `README.md` §14 ve bu dosyanın §11'i. | -| **Menus › Routes / Module / Menu / Manager** | `App.Menus.Routes` · `.Module` · `.Menu` · `.Manager` (`/admin/menuManager`) | `Sas_H_Route` · `Sas_H_Module` · `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, `Routes` yalnızca özel React sayfası eklenirken kullanılır. | -| **Developer Kit › SQL Query Manager** | `App.SqlQueryManager` · `/admin/sqlQueryManager` | — | — | View/procedure/function yazma ve çalıştırma; karar sırasının 2. adımı buradan başlar. | -| **Developer Kit › Custom Endpoints** | `App.DeveloperKit.CustomEndpoints` | `Sas_H_CustomEndpoint` | tenant | `Name`, `Url`, `Method`, `DataSourceCode`, `Sql`, `ParametersJson`, `PermissionsJson` — parametreli SQL'i yetkili bir HTTP ucuna çevirir. | -| **Developer Kit › Dynamic Services** | `App.DeveloperKit.DynamicServices` · `/admin/list/App.DeveloperKit.DynamicServices` | `Sas_H_DynamicService` | host | Liste ListForm ekranıdır; kod, listenin toolbar/satır butonlarından açılan `DynamicServiceEditor` diyalogunda (Monaco) yazılıp derlenir ve yayınlanır. SQL'in yetmediği iş kuralı için 3. adım. | -| **Developer Kit › Components** | `App.DeveloperKit.CustomComponents` · `/admin/list/App.DeveloperKit.CustomComponents` | `Sas_H_CustomComponent` | host | Custom component listesi (ListForm) ve satırdaki **Design** butonuyla **Visual Designer** girişi (§10). Kayıtlar `data/App.DeveloperKit.CustomComponents/{Id}.json` klasörüne senkronlanır. | +| **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 › Wizard Manager** | menü kodu `App.Listforms.WizardManager` · yetki `App.Listforms.Wizard` · `/admin/listform/wizardManager` | — | host | Wizard seed dosyalarının listesi, düzenlemesi, silinmesi, export/import'u. Sihirbazın kendisi ayrı bir rotadadır: `/admin/listform/wizard`. | -| **Developer Kit › MCP Servers** | `App.DeveloperKit.McpServers` · `/admin/list/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 açık), `MaxRows`. Araç listesi bu kayda göre süzülür, çağrı anında aynı kural yeniden uygulanır. Yetkiler: ekran için `App.DeveloperKit.McpServers`, araç çağırma için `.Invoke`, veri yazma için `.Write`, prompt ile tablo/ekran/bileşen geliştirme (`create_table`, wizard deploy, custom component kaydı) için `.Develop` (host; Wizard ve Custom Components yetkilerini ayrıca ister). Kiracı kullanıcısı yetkisi ne olursa olsun yalnızca okur (`McpToolNames.TenantTools`: şema araçları, `query_list`, `run_select`); yazma, `create_table`, wizard, component ve `run_query` kiracıya kapalıdır. Host için tek sunucu anahtarı `AllowedTools`. MCP yetki vermez: kullanıcı yalnızca uygulamada okuyabildiği listeleri görür ve sorgular; ham SQL `App.SqlQueryManager` ister. Kayıtlar `data/App.DeveloperKit.McpServers.json` dosyasına senkronlanır. Araçlar ve şema keşfinin kaynağı: `README.md` §13.9. | -| **Forum Management** | `App.ForumManagement` (grup menüsü) | `Sas_T_ForumCategory` · `Sas_T_ForumTopic` · `Sas_T_ForumPost` | tenant | Altında Categories / Topics / Posts ListForm ekranları; yayın tarafı Administration'daki `/admin/forum`. | -| **Forum Categories** | `App.ForumManagement.Categories` · `/admin/list/App.ForumManagement.Categories` | `Sas_T_ForumCategory` | tenant | Forum kategorilerinin ListForm ekranı; veri `configs/seeds/host/data/App.ForumManagement.Categories.json` dosyasından seed edilir. | -| **Forum Topics** | `App.ForumManagement.Topics` · `/admin/list/App.ForumManagement.Topics` | `Sas_T_ForumTopic` | tenant | Forum konuları; kategori lookup'ı, sayaçlar ve sabitle/kilitle/çözüldü bayrakları. | -| **Forum Posts** | `App.ForumManagement.Posts` · `/admin/list/App.ForumManagement.Posts` | `Sas_T_ForumPost` | tenant | Forum gönderileri; konu lookup'ı, kabul edilen yanıt bayrağı. | +| **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`, araç çağırma `.Invoke`, veri yazma `.Write`, tablo/ekran/bileşen geliştirme (`McpToolNames.DevelopTools`) `.Develop` — arkasındaki Wizard/Custom Components yetkileri ayrıca istenir. Kiracı kullanıcısı yalnızca okur (`TenantTools`: şema araçları, `query_list`, `run_select`); 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 ve izlenen kayıtları kaldırılır, başka ekran kullanıyorsa engellenir, sonuç raporlanı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. Yeni ürettiğin ekranlarda +> 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`. Bu menünün tabloları `Adm_T_*` — yani **kiracı başına** ayrışır. +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 `configs/seeds/host/data/App.Definitions.Sector.json` dosyasından seed edilir. | +| **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** | `App.Definitions.OrgChart` · `/admin/organization` | `Adm_T_Department` | Departman/ünvan verisinin organizasyon şeması görünümü. | +| **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ü). | -| **Agenda** | `App.Agenda` | `Adm_T_Agenda` | **Takvim (Scheduler) düzeninin** referansı: `StartDate`, `EndDate`, `AllDay`, `RecurrenceRule`, `RecurrenceException`. Tekrarlayan randevu isteği kod gerektirmez. | -| **Bildirimler › Bildirim Türleri / Kuralları** | `App.Notifications.NotificationTypes` · `.NotificationRules` | `Adm_T_NotificationType` · `Adm_T_NotificationRule` | Olay bazlı bildirim (7 kanal); tür ve kural kiracıya aittir, yeni kiraciya varsayılan küme kopyalanır. Ayrıntı: §9.11, `README.md` §13.5.1. | -| **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 (`list-form-dynamic-api/notification-insert`). Ayrı bir manuel gönderim tablosu yoktur. Ayrıntı: `README.md` §13.5.1. | -| **Bildirimler › Bildirim Şablonları** | `App.Notifications.NotificationTemplates` | `Adm_T_NotificationTemplate` | Elle gönderimde seçilen mesaj şablonları (kanal, konu, gövde, yer tutucular); şablon seçilince konu ve gövde forma kopyalanır. Örnek satırlar seed ile gelir. | +| **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 (+Like/Comment)** | `App.Intranet.Announcement` | `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 (+Question/Option/Response/Answer)** | `App.Intranet.Survey` | `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 (+Comment/Like)** | `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 (+Like/Comment)** | `App.Intranet.Events.*` | `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ı. `EventComment` menüde Intranet altında (Documents'ın üstünde), `EventPhoto` menüde yer almaz; ikisinin yetkisi de `App.Intranet.Events.Event` altındadır. `Adm_T_EventPhoto` tablosu `configs/seeds/host/sql/object/Adm_T_EventPhoto.sql` ile oluşturulur. | +| **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). | +| **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. | -| **Identity › Users** | `AbpIdentity.Users` | `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 › 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 (§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 (§9.12). | +| **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ı. | -| **Files** | `App.Files` · `/admin/files` | — | Dosya yöneticisi. Ekranın dosya alanı için ayrı depolama kurma; dosya/görsel editörleri bu altyapıyı kullanır. | | **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` ve dinamik ekran rotaları. +`/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: @@ -1258,10 +1335,15 @@ açılmaz: ### 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. +`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). +`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 | | --- | --- | @@ -1284,29 +1366,35 @@ görünürlük `checkPermission` ile bileşen içinde kurulur (`lowcode` §7.5). | `/admin/reports/:report/{view,design}/:id?/:listFormCode?` | `DevexpressReportViewer` · `DevexpressReportDesigner` | — | | `/admin/settings` | `settings/Settings` | `App.Setting` | | `/admin/files` | `admin/files/FileManager` | `App.Files` | -| `/admin/ous` · `/admin/organization` | `organization-unit/OrganizationUnits` · `hr/OrgChart` | `Abp.Identity.OrganizationUnits` · `App.Definitions.Department` | +| `/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** (`routeType: public`) — genel site. `/home` · `/about` · -`/contact` içerikleri kod değil, DeveloperKit > Custom Components (Visual Designer) altındaki -tasarım belgesinden gelir (README.md §10.4); bir "tanıtım sayfası" isteğinde yeni React sayfası -yazma, oradan yeni bir Custom Component üret. `Services` sayfası yoktur. +**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` · tasarımcı: DeveloperKit > Custom Components | +| `/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ı. Listeye yeni bir public -yol eklediğinde bu alanları da doldur (lowcode §0.9). +ü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` → @@ -1320,15 +1408,20 @@ yol eklediğinde bu alanları da doldur (lowcode §0.9). | --- | --- | --- | | "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" | Saas › Notifications (+ MailQueue §11) | Tür + kural; e-posta ise MailQueue | +| "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" | Administration › Files | Dosya/görsel editörü; ayrı depolama kurma | +| "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ı + `LanguagesData.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ı | @@ -1353,9 +1446,11 @@ içinde ilgili `#region` altındadır. | 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` — `WidgetsJson`; sorgu `Title`, `Value`, `Color`, `SubTitle`, `Icon` kolonlarını döndürür | +| 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}"` | +| 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) | diff --git a/.github/instructions/lowcode.instructions.md b/.github/instructions/lowcode.instructions.md index ff5c8bc2..6ac1b5e1 100644 --- a/.github/instructions/lowcode.instructions.md +++ b/.github/instructions/lowcode.instructions.md @@ -52,9 +52,10 @@ configs/seeds/host/ ├── sql/execute/{Ad}.sql ← bir kez çalışacak script (veri dolumu, migrasyon) ├── postgres/object|execute/… ← aynısının PostgreSQL diyalekti (hedef PG ise) ├── crud/{EntityName}.json ← tablonun REST uçları -├── wizard/{WizardName}.json ← ekran + menü + yetki + dil anahtarları -├── data/{ListFormCode}.json ← ekrandan girilen liste verisinin aynası (opsiyonel) -│ Custom Component'ler de burada yaşar: +├── wizard/{WizardName}.json ← ekran + menü + yetki + dil anahtarları (List yolunda sunucu yazar, §4.1) +├── data/{ListFormCode}.json ← ekrandan girilen liste verisinin aynası (opsiyonel, §9.15) +│ data/{ListFormCode}/{anahtar}.json ← aynı aynanın klasör modu (satır başına dosya) +│ Custom Component'ler klasör modunda yaşar: │ data/App.DeveloperKit.CustomComponents/{Id}.json ``` @@ -67,9 +68,9 @@ Tenant kapsamı isteniyorsa `host/` yerine `tenants/{tenantId}/`; iç düzen ayn | 1 | Tablo / view | `sql/object/…` | Ekranın bağlanacağı nesne önce var olmalı. MCP'de yeni tablo `create_table` ile kurulur (script ve dosya tasarımcıyla aynı); view MCP'den kurulamaz | | 2 | CRUD uçları (gerekiyorsa) | `crud/{Entity}.json` | Custom Component'in data source'ları buna bakar | | 3 | Custom Component (gerekiyorsa) | `data/App.DeveloperKit.CustomComponents/{Id}.json` | Wizard `Custom` yolunda bileşen adını arar | -| 4 | Wizard | `wizard/{Ad}.json` | Menü, yetki, dil anahtarı ve ListForm'u üretir | +| 4 | Wizard | `wizard/{Ad}.json` | Menü, yetki, dil anahtarı ve ListForm'u üretir. List ekranı **deploy** ile kurulur (MCP `deploy_wizard` ya da wizard ekranı, §0.10); dosyayı sunucu yazar. Elle yazılan List dosyası `ListForm` + `Fields` taşımak zorundadır, yoksa seeder atlar (§4.1) | | 5 | Alt ekranlar (varsa) | ek `wizard/…` dosyaları | Ana wizard'dan **önceki** `SeededAt` damgasını alır | -| 6 | Liste verisi (isteniyorsa) | `data/{ListFormCode}.json` | Ekran ve tablo hazır olmalı; `ListForm.SeedFilePath` bu yolu gösterir | +| 6 | Liste verisi (isteniyorsa) | `data/{ListFormCode}.json` | Ekran ve tablo hazır olmalı; `ListForm.SeedFilePath` bu yolu gösterir (§9.15) | | 7 | SEO alanları (public yol ürettiysen) | Yolu tanımlayan satırın kendisi: `data/App.Menus.Routes.json` ya da `data/App.DeveloperKit.CustomComponents/{Id}.json` | Yolu üreten dosya yazıldıktan sonra; ziyaretçiye açık hiçbir yol metasız kalmaz (§0.9) | **`Wizard.SeededAt` bir bağımlılık aracıdır.** Dosya adı tarih taşımaz (`{WizardName}.json`); @@ -82,10 +83,13 @@ damgala. Uydurma. Şu üçünü mutlaka kontrol et: -1. **Tablo/kolon adları** — `sql/object/` altındaki dosyalar ya da mevcut wizard dosyalarındaki - `SelectCommand`. Kolon adını tahmin etme; yoksa tabloyu da sen üret. -2. **Var olan menü kökü ve izin grubu** — aynı modül için ikinci bir kök menü açma. Mevcut - wizard dosyalarındaki `MenuParentCode` / `PermissionGroupName` değerlerini tara. +1. **Tablo/kolon adları** — `sql/object/` altındaki dosyalar, mevcut wizard dosyalarındaki + `SelectCommand` ya da (MCP varsa) `describe_list`/`search_schema`. Kolon adını tahmin etme; + yoksa tabloyu da sen üret. +2. **Var olan menü kökü ve izin grubu** — aynı modül için ikinci bir kök menü açma. + `data/App.Menus.Menu.json` (platform menüleri), `data/Abp.Identity.PermissionGroups.json` ve + mevcut wizard dosyalarındaki `MenuParentCode` / `PermissionGroupName` değerlerini tara + (MCP'de `list_wizards`). 3. **Dil anahtarı çakışması** — `LanguagesData.json` ve diğer wizard dosyaları. Var olan bir anahtarın metnini seed **ezmez**; yanlış metinle karşılaşırsan anahtarı değiştir. @@ -93,9 +97,9 @@ Uydurma. Şu üçünü mutlaka kontrol et: - Değişikliklerin devreye girmesi için **DB Migrate** (SQL Query Manager / Wizard Manager üzerindeki buton, yetki `App.Setup.Migrate`) ya da migrator konteyneri (`SEED=true`) çalışmalı. -- Seeder **idempotenttir**: ekran zaten varsa dosya atlanır (§4.5). Var olan bir ekranı - değiştirmek için dosyayı düzenlemek yetmez — kayıtları silip yeniden seed etmek ya da - ekranı Wizard'dan `EditFileName` ile yeniden çalıştırmak gerekir. +- Seeder **idempotenttir**: ekran zaten varsa dosya atlanır, yalnızca eksik dil metinleri eklenir + (§4.5). Kurulu bir ekranı değiştirmek için dosyayı düzenlemek yetmez — ListForm editöründen + değiştir (dosyaya geri yazılır) ya da wizard'ı `EditFileName` ile yeniden deploy et. - Ürettiğin dosyaları kullanıcıya **tek tek listele**: hangi dosya, ne üretiyor, hangi menüde görünecek, hangi yetkiyi ister. @@ -198,7 +202,7 @@ Sütun listesi **en kritik olanıdır** — onsuz üretilen ekran her zaman yeni | **Zorunluluk ve doğrulama** | Hangileri zorunlu? Aralık/desen/uzunluk kuralı olan var mı? | `NOT NULL` kolonlar zorunlu, başka kural yok | | **Lookup'lar** | Açılır listeler nereden beslenecek: sabit liste mi, başka tablo mu, var olan tanım tablosu mu (ülke/para birimi/departman…)? Bağlı liste (il→ilçe) var mı? | Sabit liste; FK varsa hedef tabloya `Query` | | **Anahtar** | Anahtar kolon ve tipi | `Id` / `UNIQUEIDENTIFIER` (`KeyFieldDbSourceType: 9`) | -| **Görünümler** | Izgara dışında hangi görünüm açılsın: kart, pivot, grafik, ağaç, gantt, takvim, kanban? Varsayılan hangisi? | Yalnız `Grid` | +| **Görünümler** | Varsayılan dörtlüden (Grid · Card · Pivot · Chart) kapatılacak olan var mı? Ağaç, gantt, takvim, kanban gerekiyor mu? Açılışta hangisi? | `Grid` + `Card` + `Pivot` + `Chart`, `DefaultLayout: "grid"` (§9.8) | | **Görünüm eşlemeleri** | Açılan her görünümün zorunlu alanları: ağaçta `ParentId`, takvimde başlangıç/bitiş, kanbanda durum kolonu (§9.8) | Görünüm açılmaz | | **Form düzeni** | Alanlar hangi gruplara ayrılsın, grup başına kaç kolon? Popup mu satır içi mi? | Tek grup, 2 kolon, popup | | **Otomatik dolan alanlar** | Kullanıcı/tarih/tenant dışında otomatik dolacak alan var mı? Belge numarası gerekiyor mu (§9.9)? | Audit alanları + tenant | @@ -272,7 +276,7 @@ Veri kaynağı : Tablo/view adı · yoksa "üretilecek" + hangi kolonlarla Anahtar : Kolon + tip (DbType numarası) Sütunlar : Alan → editör tipi → zorunlu mu → lookup kaynağı (List yolu) Bölgeler : Bölge → bileşen → veri kaynağı → filtre (Custom yolu) -Layout(lar) : Grid / Card / Pivot / Chart / Tree / Gantt / Scheduler / Todo + varsayılan +Layout(lar) : Grid + Card + Pivot + Chart (varsayılan dörtlü) [+ Tree / Gantt / Scheduler / Todo] + açılış görünümü Form düzeni : Gruplar ve kolon sayısı, düzenleme modu Otomatikler : Varsayılan değerler, numaratör, hesaplar (EditorScript) Doğrulama : Alan kuralları + sunucu tarafı kontrol @@ -336,6 +340,9 @@ tahmine değil, gördüğün öğelere dayandır. | Onayla / Reddet butonları | `WorkflowJson` (otomatik gelir, elle tanımlama) | | Yan yana bağımsız bölümler, karışık yerleşim | Custom Component · `FlexRow` + düğümler | +Görselde yalnız tablo görünmesi Card/Pivot/Chart'ı kapatmak değildir; liste ekranı varsayılan +dörtlüyle üretilir (§9.8), görseldeki görünüm `DefaultLayout` olur. + #### 0.7.3 Görseldeki kontrol → `EditorType` | Görselde | `EditorType` | Ek | @@ -484,6 +491,26 @@ alanıyla ziyaretçiye taşınır (`PlatformApplicationConfigurationContributor` JSON-LD belgelerini kurar; `::` ile başlayan değerler orada çevrilir (`resolveSeoText`). `sitemap.xml` ve `robots.txt` aynı kaynaktan `SeoController` tarafından üretilir. +### 0.10 MCP ile üretim — geliştirme araçları + +`/mcp` ucuna bağlı bir istemci ekranı dosya yazarak değil, uygulamanın kendi servislerinden geçen +araçlarla üretir; sonuç ve seed dosyaları wizard ekranıyla birebir aynıdır. Araç adları +`McpToolNames`'tedir; geliştirme araçlarının hepsi `App.DeveloperKit.McpServers.Develop` ister, +kiracı kullanıcısına hiçbiri açılmaz. + +| Araç | Ne yapar | Not | +| --- | --- | --- | +| `get_dev_guide` | Bu dosyanın (`lowcode`) ya da `reference`'ın başlık listesini / bir bölümünü döner | Bölüm numarayla istenir (`4.3`); aynı numarayı iki başlık taşımamalıdır | +| `create_table` | Yeni SQL Server tablosu kurar, `sql/object/{Tablo}.sql` dosyasını yazar | Ek olarak `App.SqlQueryManager` ister; `Id`/`TenantId`/audit kolonlarını kendi ekler (`fullAudited`, `multiTenant` varsayılan açık); var olan tabloyu reddeder. View/SP/fonksiyon kurulamaz | +| `list_wizards` · `get_wizard` | Wizard dosyalarını listeler (menü yolu, bağımlılar) · dosyayı diskteki hâliyle döner | Var olan menü kökünü/izin grubunu bulmak için önce `list_wizards` | +| `deploy_wizard` | Wizard cevabından ekranı, menüyü, yetkileri, dil anahtarlarını ve seed dosyasını üretir | Girdi §4.4'teki düz cevap ya da `get_wizard` çıktısıyla aynı dosya biçimi; enum'lar sayı. `replaceFileName` = `EditFileName` (§4.5) | +| `delete_wizard` | Wizard'ı ve yalnızca onun ürettiği kayıtları siler | Bağımlı wizard varsa reddeder (§4.5) | +| `list_components` · `get_component` · `save_component` · `delete_component` | Custom component okuma/yazma (ada göre upsert) | `save_component` visual designer bileşenini reddeder — yalnız Code modu (§7.1.1) | + +Okuma araçları (`list_lists`, `describe_list`, `search_schema`, `query_list`, `run_select`, +`list_queries`, `run_query`) keşif turunda (§0.3) tablo/kolon/lookup kaynağını bulmak içindir; +kullanıcının uygulamadaki yetkisini aşmaz. + ## 1. Hangi istek → hangi artefakt | İstek | Üretilecek artefakt | Bölüm | @@ -539,8 +566,9 @@ Tutarlılık zorunludur; menü, route, yetki ve dil anahtarı aynı kökten tür | Route (List) | `/admin/list/{ListFormCode}` — statik `:listFormCode` route'u karşılar, DB'ye kayıt yazılmaz | `/admin/list/App.Mrp.Orders` | | Route (Custom) | Bileşenin `RoutePath` değeri; route doğrudan bundan üretilir | `/admin/order-board` | | Wizard seed dosyası | `{WizardName}.json` | `Orders.json` | -| Custom component seed | `{Name}.json` | `OrderBoard.json` | -| CRUD endpoint seed | `{EntityName}.json` | `Mrp_T_Order.json` | +| Custom component seed | `data/App.DeveloperKit.CustomComponents/{Id}.json` — dosya adı satırın `Id`'si, bileşen adı satırın `Name` alanı | `…/8389ccbc-3e7b-….json` | +| CRUD endpoint seed | `crud/{EntityName}.json` | `Mrp_T_Order.json` | +| Liste verisi seed | `data/{ListFormCode}.json` (tek dosya) ya da `data/{ListFormCode}/{anahtar}.json` (klasör) — §9.15 | `App.Definitions.Sequence.json` | Kapsam klasörü: host bağlamında `configs/seeds/host/…`, tenant bağlamında `configs/seeds/tenants/{tenantId}/…`. Çözüm her zaman `SeedPathResolver` üzerinden yapılır; @@ -571,6 +599,8 @@ yol elle kurulmaz. **`WorkerType`** (`WorkerTypeEnum`) — `1` MailQueueWorker · `2` SqlWorker · `3` NotificationWorker · `4` SessionCleanupWorker · `5` BackupWorker (§9.10) +**`Scope`** (`WorkerScopeEnum`) — `1` Host · `2` Tenant · `3` All (önce host, sonra her kiracı) (§9.10) + **`ResetPeriod`** (`SequenceResetPeriod`) — `0` None · `1` Yearly · `2` Monthly · `3` Daily · `4` Hourly · `5` Minutely (§9.9) @@ -591,6 +621,8 @@ yazılır: | `ListFormType` (`ListFormTypeEnum`) | `List` · `Form` · `Chart` | | `SubForms[].TabType` (`ListFormTabTypeEnum`) | `List` · `Card` · `Tree` · `Gantt` · `Scheduler` · `Todo` · `Form` · `Chart` · `Pivot` | | `DefaultLayout` | `grid` · `card` · `pivot` · `chart` · `tree` · `gantt` · `scheduler` · `todo` (küçük harf) | +| Bildirim kanalı (`NotificationChannels`) | `Mail` · `Sms` · `WhatsApp` · `Telegram` · `Rocket` · `Desktop` · `UiToast` · `UiActivity` — etiketleri `App.NotificationChannel.{Kanal}` dil anahtarlarıdır | +| Bildirim alıcı tipi (`NotificationRecipientTypes`) | `User` · `Role` · `OrganizationUnit` · `All` · `Custom` | **`DbSourceType` / `KeyFieldDbSourceType`** (`System.Data.DbType`): @@ -634,21 +666,28 @@ Her editörün kendine özgü seçenekleri için §4.3.2. ## 4. Artefakt A — ListForm ekranı (Wizard seed dosyası) Konum: `configs/seeds/{kapsam}/wizard/{WizardName}.json` (dosya adında tarih yoktur) -Okuyan: `WizardDataSeeder` · Yazan: `ListFormWizardAppService` ve `WizardSeedSynchronizer` +Okuyan: `WizardDataSeeder` · Yazan: `ListFormWizardAppService` (deploy) ve `WizardSeedSynchronizer` +(ListForm editöründen kayıt) · Okuma/yazma her zaman `WizardSeedFileDto.FromSeedJson` / +`ToSeedJson` ile — düz `JsonSerializer` bloklu bölümleri düşürür. + +**İki biçim vardır, karıştırma:** *wizard cevabı* (düz `ListFormWizardDto`, §4.4) deploy'un +girdisidir — wizard ekranı ve MCP `deploy_wizard` bunu alır; *seed dosyası* (§4.1) deploy'un +çıktısıdır. List yolunda dosyayı deploy'a ürettir; `ListForm` bölümü olmayan List dosyası +seeder tarafından uyarıyla **atlanır**. ### 4.1 Dosya iskeleti ```jsonc { "Wizard": { // yalnızca ekrandan üretilemeyen bilgiler — 4.2 - "ComponentKind": 0, "WizardName": "Tags", "SeededAt": "2026-09-01T09:03:00", - "WizardCode": "App.Prj.Tags", "CustomComponentName": "", - "PermissionGroupName": "App.Prj", "DataSourceConnectionString": "", "ModuleId": "Prj", + "ComponentKind": 0, "WizardName": "Tags", "WizardCode": "App.Prj.Tags", + "SeededAt": "2026-09-01T09:03:00", "CustomComponentName": null, + "PermissionGroupName": "App.Prj", "ModuleId": "Prj", // boş bağlantı metni yazılmaz "Menu": { "MenuCode": "…", "MenuOrder": 2, "MenuUrl": "…", "MenuIcon": "…", "MenuParentCode": "…", "MenuParentIcon": "…" }, // "CreateMenu": false ise eklenir "Language": { "MenuEn": "…", /* Menu/Title/Desc/MenuParent/PermissionGroup · EN + TR */ }, "Groups": [ { "Items": [ { "FieldName": "…", "CaptionName": "…", - "En": "…", "Tr": "…" } ] } ] + "En": "…", "Tr": "…" } ] } ] // grup Caption/ColCount yok }, "ListForm": { /* GridOptionsEditDto — ekranın kendisi, ListForm detayıyla aynı sözleşme */ }, "Fields": [ /* ColumnFormatEditDto — alanlar, alan detayıyla aynı sözleşme */ ], @@ -663,13 +702,22 @@ Okuyan: `WizardDataSeeder` · Yazan: `ListFormWizardAppService` ve `WizardSeedSy `Wizard` bölümü yalnızca ekrandan üretilemeyen bilgileri taşır (menü, yetki, dil metinleri, bileşen yolu, alan başlıklarının dil karşılıkları); `ListForm`/`Fields` içinde karşılığı olan -hiçbir alan orada tekrar edilmez (§4.2). +hiçbir alan orada tekrar edilmez (§4.2). Anahtar sırası sabittir: `ComponentKind`, `WizardName`, +`WizardCode`, `SeededAt`, `CustomComponentName`, `PermissionGroupName`, `ModuleId`, +`DataSourceConnectionString`, `Menu`, `Language`, `Groups`. `ListForm`/`Fields` içindeki +`Id`/`CreationTime`/`EditType` gibi çalışma zamanı alanları sıfırlanmış yazılır. + +**Custom yolunda** (`ComponentKind: 1`) `ListForm` `null`, `Fields` `[]` yazılır ve kırpma +yapılmaz: `Wizard` bloğu List'e ait anahtarları (`Grid`/`Card`/…, `SelectCommand`, option +blokları) varsayılan değerleriyle taşımaya devam eder; bunlar kullanılmaz, temizlemeye çalışma. +Bu yolun dosyası elle yazılabilir — seeder yalnızca menü, yetki ve dil kaydı üretir. **`ListForm` + `Fields` ekranın tek gerçeğidir.** Seeder ekranı bu iki bölümden kurar; `Wizard` bölümünden yalnızca menü, yetki, dil anahtarları ve veri kaynağı üretilir. Ekran **ListForm detayından** (liste ayarları, alan ayarları, JSON satır editörleri) düzenlendiğinde sunucu aynı dosyanın `ListForm`/`Fields` bölümlerini ve `Wizard` bölümündeki karşılıklarını geri yazar -(`WizardSeedSynchronizer`); böylece wizard yeniden deploy edildiğinde tasarımcıdaki iş kaybolmaz. +(`WizardSeedSynchronizer`); böylece migrate ya da import ekranı dosyadan kurarken tasarımcıdaki iş +kaybolmaz (`EditFileName` ile yeniden deploy ise ekranı cevaptan üretir, §4.5). Elle bu dosyayı yazarken `ListForm`/`Fields` bölümlerini uydurma — wizard'ı deploy et, dosyayı sunucu üretsin. @@ -709,16 +757,16 @@ saklanır. Dosya okunurken bu alanlar `ListForm`/`Fields` üzerinden geri doldur | `MenuCode` | Menü kaydının kodu; yetki adlarının kökü de budur. | | `MenuUrl` | List'te `/admin/list/{MenuCode}` hesaplanır; Custom'da bileşenin `RoutePath`'i yazılır. | | `CustomComponentName` | Yalnızca Custom yolunda; bağlanacak bileşenin adı. | -| `EditFileName` | Yalnızca **istek girdisi**: doluysa güncellemedir, sunucu önce eski dosyayı ve ürettiği kayıtları siler. Seed dosyasına yazılmaz. | +| `EditFileName` | Yalnızca **istek girdisi** (MCP'de `replaceFileName`): doluysa güncellemedir — `.Update` yetkisi ister, `SeededAt` korunur, eski dosya ve ürettiği kayıtlar `Redeploy` kipinde silinip ekran **cevaptan yeniden üretilir**; ListForm editöründe yapılmış, wizard'da karşılığı olmayan ayarlar kaybolur. Seed dosyasına yazılmaz. | **Menü** -| Alan | Anlam | -| --- | --- | > Dosyada bu bloğun tamamı `Menu` başlığı altındadır; `MenuParentModuleId` ise kök seviyede > `ModuleId` adıyla yazılır. Menüsüz wizard'da da blok korunur: `MenuCode` yetkilerin kökü, > Custom yolunda `MenuUrl` rotanın kaynağıdır. +| Alan | Anlam | +| --- | --- | | `CreateMenu` | `false` ise menü ve üst menü kaydı **hiç üretilmez**; ListForm, yetki ve dil anahtarları yine üretilir. SubGrid/parça olarak kullanılacak ekranlar için. Varsayılan `true` olduğu için dosyaya **yalnızca `false` iken** yazılır. | | `MenuParentCode` / `MenuParentModuleId` / `MenuParentIcon` | Üst menü; yoksa oluşturulur. | | `MenuIcon` | React-icons adı (`FcBiohazard`, `FaBox`…). | @@ -726,13 +774,13 @@ saklanır. Dosya okunurken bu alanlar `ListForm`/`Fields` üzerinden geri doldur **Yetki ve dil** -| Alan | Anlam | -| --- | --- | > Dil metinlerinin tamamı dosyada `Language` başlığı altındadır; başlık zaten bölümü işaretlediği > için anahtarlar ortak `LanguageText` öneki olmadan yazılır (C# sözleşmesinde önek durur). > `ListForm` yalnızca dil **anahtarını** taşır (`Title = "{ListFormCode}.Title"`), metinlerin tek > kaynağı burasıdır. +| Alan | Anlam | +| --- | --- | | `PermissionGroupName` | Var olan grup ya da yeni grup adı. | | `Language.PermissionGroupEn` / `Tr` | Grup adıyla aynı dil anahtarına yazılır. Var olan grup seçilip boş bırakılırsa sunucu veritabanındaki değeri geri yazar. | | `Language.MenuEn` / `Tr` | Menü etiketi. | @@ -741,8 +789,8 @@ saklanır. Dosya okunurken bu alanlar `ListForm`/`Fields` üzerinden geri doldur | `Language.MenuParentEn` / `Tr` | Üst menü etiketi. | Üretilen yetkiler: `{MenuCode}` (okuma, kök) + List yolunda `.Create .Update .Delete .Export -.Import .Note`, Custom yolunda yalnızca `.Create .Update .Delete`. Hepsi admin rolüne grant -edilir. +.Import .Note`, Custom yolunda yalnızca `.Create .Update .Delete`. Deploy hepsini admin rolüne +grant eder; `WizardDataSeeder` grant yazmaz — migrate ile gelen ekranın rol yetkisi ayrıca verilir. **Veri** @@ -752,7 +800,7 @@ edilir. | `DataSourceConnectionString` | Yalnızca **yeni** bir bağlantı tanımlanıyorsa doldurulur; boşsa dosyaya hiç yazılmaz. **Asla gerçek kimlik bilgisi yazma.** | | `SelectCommandType` | `[ListForm]` Bkz. §3. | | `SelectCommand` | `[ListForm]` Tablo/view adı, fonksiyon çağrısı ya da sorgu metni. | -| `KeyFieldName` / `KeyFieldDbSourceType` | `[ListForm]` Birincil anahtar ve tipi. | +| `KeyFieldName` / `KeyFieldDbSourceType` | `[ListForm]` Birincil anahtar ve tipi. Wizard ekranının başlangıç değeri `11` (Int32), C# DTO'nun varsayılanı yok — her zaman açıkça yaz; `Guid` anahtarda `9`. | **Davranış** @@ -762,7 +810,7 @@ edilir. | `AllowAdding` / `AllowUpdating` / `AllowDeleting` / `ConfirmDelete` | `[ListForm]` Toolbar ve satır aksiyonları (`EditingOptionDto`). | | `AllowDetail` | `[ListForm]` Satırda **Detay** düğmesi çıkar; `/admin/form/{ListFormCode}/{anahtar}` adresini yeni sekmede açar — aynı ekranın tek kayıtlık form görünümüdür. `KeyFieldName` boşsa çalışmaz. | | `DefaultLayout` | `[ListForm]` `"grid"`, `"card"`, `"pivot"`, `"chart"`, `"tree"`, `"gantt"`, `"scheduler"`, `"todo"`. | -| `Grid` `Card` `Pivot` `Chart` `Tree` `Gantt` `Scheduler` `Todo` | `[ListForm]` Hangi görünümlerin açık olduğu (`LayoutDto`). Açtığın her görünümün option bloğunu da doldur. | +| `Grid` `Card` `Pivot` `Chart` `Tree` `Gantt` `Scheduler` `Todo` | `[ListForm]` Hangi görünümlerin açık olduğu (`LayoutDto`). **Liste ekranında `Grid`/`Card`/`Pivot`/`Chart` varsayılan `true`**, diğer dördü `false` (§9.8). Açtığın her görünümün option bloğunu da doldur. | **Görünüm option blokları** — `[ListForm]`; yalnızca ilgili bayrak `true` ise anlamlıdır: @@ -889,11 +937,17 @@ sayı, `multiple` bir boolean olmalıdır. { "FieldName": "StatusId", "EditorType": "dxSelectBox", "DbSourceType": 11, "LookupDataSourceType": 1, - "LookupQuery": "[{\"Key\":1,\"Name\":\"Taslak\"},{\"Key\":2,\"Name\":\"Onayda\"},{\"Key\":3,\"Name\":\"Onaylandı\"}]", + "LookupQuery": "[{\"key\":1,\"name\":\"Taslak\"},{\"key\":2,\"name\":\"Onayda\"},{\"key\":3,\"name\":\"Onaylandı\"}]", "ValueExpr": "Key", "DisplayExpr": "Name" } ``` +İstemci `ValueExpr`/`DisplayExpr`'i küçük harfe çevirerek okur; `StaticData` dizisi JSON'dan olduğu +gibi yüklendiği için öğe anahtarları **küçük harf** (`key`, `name`, cascade'de `group`) yazılır. +`name` değeri `::` öneki eklenerek çevrilir — dil anahtarı da yazılabilir. `Query` sonucunda ise +istemci `Key`/`Name`/`Group` kolonlarını `key`/`name`/`group`'a eşler; sorgu kolonları bu adlarla +döndürür (`SELECT Id AS [Key], Name AS [Name] …`) ve `ValueExpr`/`DisplayExpr` `Key`/`Name` kalır. + `LookupDataSourceType` `1` (StaticData) ise `LookupQuery` bir JSON dizisi, `2` (Query) ise SQL, `3` (WebService) ise noktalı virgülle ayrılmış bir çağrı tarifidir. Tam sözleşme ve cascade (ebeveyn–çocuk) kurulumu için §4.3.4. @@ -947,40 +1001,44 @@ Wizard'daki `LookupDataSourceType` + `LookupQuery` + `ValueExpr` + `DisplayExpr` | `DataSourceType` | `1` StaticData · `2` Query · `3` WebService | | `ValueExpr` / `DisplayExpr` | Varsayılan `Key` / `Name` | | `LookupQuery` | Kaynağın kendisi (tipe göre biçim değişir, aşağıda) | -| `CascadeParentFields` | **Ebeveyn alanda** doldurulur; bu alan değişince listesi yenilenecek çocuk alan(lar), virgülle | -| `CascadeRelationField` | **Çocuk alanda** doldurulur; ebeveynin değeriyle karşılaştırılacak alan | -| `CascadeFilterOperator` | Cascade filtresinin operatörü; varsayılan `=` | -| `CascadeEmptyFields` | Ebeveyn değişince içeriği boşaltılacak alanlar, virgülle | +| `CascadeParentFields` | **Çocuk alanda** doldurulur; değerleri sorguya taşınacak ebeveyn alan(lar), virgülle. Sırayla `@param0`, `@param1`… olur (`Query`/`WebService`); gelmeyen parametre `NULL` bağlanır | +| `CascadeRelationField` | **Çocuk alanda** doldurulur; `StaticData`'da ebeveynin değeriyle karşılaştırılan alan (öğedeki `group`) | +| `CascadeFilterOperator` | `StaticData` cascade filtresinin operatörü; varsayılan `=` | +| `CascadeEmptyFields` | **Ebeveyn alanda** doldurulur; bu alan değişince boşaltılacak çocuk alanlar, virgülle. Ara halka (İl) hem çocuk hem ebeveyn olduğu için iki tarafı birden taşır | **`LookupQuery` biçimleri:** | Tip | Biçim | Not | | --- | --- | --- | | `1` StaticData | `[{"key":1,"name":"Taslak"},{"key":2,"name":"Onayda"}]` | Kolon **sayı** tipindeyse `key` sayı, **metin** tipindeyse `key` metin olmalıdır; aksi hâlde seçim eşleşmez. Cascade gerekiyorsa her öğeye `group` eklenir. | -| `2` Query | `SELECT Id AS [Key], Name FROM ... WHERE IsDeleted = 0` | Cascade için ayrıca ebeveyn değerini taşıyan bir `group` kolonu döndürülmelidir. | +| `2` Query | `SELECT Id AS [Key], Name AS [Name] FROM ... WHERE IsDeleted = 0` | Cascade'de ebeveyn değerleri `@param0…` ile gelir; boş ebeveyn tüm listeyi göstersin diye `(Kolon = @param0 OR @param0 IS NULL)` kalıbı kullanılır. Sorgu ekranın veri kaynağında/tenant bağlamında çalışır, `@USERID` gibi token'lar çözülür. | | `3` WebService | `METHOD;url;bodyJson;keySelector;nameSelector;groupSelector` | Noktalı virgülle **altı parça**. Selector'lar her satır `a` üzerinde çalışan JS ifadeleridir (`a.id`, `a.name`, `null`). Gövdedeki `@param0`, `@param1`… cascade filtre değerleriyle doldurulur. Örnek: `GET;notification-rule/notification-types;{};a.id;a.name;null` | -Cascade örneği — İl → İlçe (aşağıdaki nesneler `LookupJson` kolonuna **tek satır, kaçışlanmış -JSON string** olarak yazılır): +Cascade örneği — Ülke → İl → İlçe (aşağıdaki nesneler `LookupJson` kolonuna **tek satır, +kaçışlanmış JSON string** olarak yazılır; canlı örnek Tenants ekranı, `LookupQueryValues.CityValues` +/ `DistrictValues`): ```jsonc -// İl alanı (ebeveyn) -{ - "DataSourceType": 2, "ValueExpr": "Key", "DisplayExpr": "Name", - "LookupQuery": "SELECT Id AS [Key], Name FROM Def_T_City", - "CascadeParentFields": "DistrictId", - "CascadeEmptyFields": "DistrictId" -} +// Country alanı (yalnız ebeveyn) +{ "DataSourceType": 2, "ValueExpr": "Key", "DisplayExpr": "Name", + "LookupQuery": "SELECT Id AS [Key], Name AS [Name] FROM Sas_H_Country WHERE IsDeleted = 'false' ORDER BY Name", + "CascadeEmptyFields": "City,District" } -// İlçe alanı (çocuk) -{ - "DataSourceType": 2, "ValueExpr": "Key", "DisplayExpr": "Name", - "LookupQuery": "SELECT Id AS [Key], Name, CityId AS [group] FROM Def_T_District", - "CascadeRelationField": "CityId", - "CascadeFilterOperator": "=" -} +// City alanı (Country'nin çocuğu, District'in ebeveyni) +{ "DataSourceType": 2, "ValueExpr": "Key", "DisplayExpr": "Name", + "LookupQuery": "SELECT Name AS [Key], Name AS [Name] FROM Sas_H_City WHERE (Country = @param0 OR @param0 IS NULL) AND IsDeleted = 'false' ORDER BY Name", + "CascadeParentFields": "Country", "CascadeRelationField": "Country", "CascadeFilterOperator": "=", + "CascadeEmptyFields": "District" } + +// District alanı (yalnız çocuk) — iki ebeveyn → @param0, @param1 +{ "DataSourceType": 2, "ValueExpr": "Key", "DisplayExpr": "Name", + "LookupQuery": "SELECT Name AS [Key], Name AS [Name] FROM Sas_H_District WHERE (Country = @param0 OR @param0 IS NULL) AND (City = @param1 OR @param1 IS NULL) AND IsDeleted = 'false' GROUP BY Name ORDER BY Name", + "CascadeParentFields": "Country,City", "CascadeRelationField": "City", "CascadeFilterOperator": "=" } ``` +Tablo adlarını tahmin etme: platform tablolarının adı `TableNameResolver`'dan gelir (reference +§12.1); yukarıdakiler kalıbı gösterir, üretimden önce `sql/object/` ya da mevcut sorgudan doğrula. + > Lookup verisi önbelleklenir; aynı sorgu aynı filtrelerle bir kez yüklenir. ### 4.3.5 Varsayılan değerler — `*FieldsDefaultValueJson` @@ -1112,14 +1170,21 @@ varsayılan değeri olur ve **yeni kayıtta o alanı otomatik doldurur**. - `DefaultValue` dolu ise seçim temizlenemez ve liste ilk açılışta o değerle gelir. - Ek filtre ile yönetilen alan, yeni kayıt varsayılanlarında **atlanır** (§4.3.5) — değeri filtreden gelir. -### 4.4 Çalışan örnek — tablo üzerinden liste ekranı +### 4.4 Çalışan örnek — wizard cevabı (deploy girdisi) + +Aşağıdaki **düz** biçim wizard ekranının POST ettiği ve MCP `deploy_wizard`'ın aldığı cevaptır +(`ListFormWizardDto`; `Groups[].Items[]` = `WizardColumnItemInputDto`, `En`/`Tr` = C#'taki +`EnglishCaption`/`TurkishCaption`). Okuyucu `FromSeedJson` olduğu için `Menu`/`Language` blokları +ya da `WizardCode`/`ModuleId` adları da kabul edilir. Deploy bunu okuyup ekranı kurar ve §4.1 +biçimindeki dosyayı yazar; `IsDeletedField`/`IsCreatedField` tablodaki `IsDeleted`/`CreatorId` +kolonlarından, `InsertedRecords` gerçekten yaratılan kayıtlardan **sunucu tarafından** doldurulur — +cevaba yazılmaz. ```jsonc { "Wizard": { "ComponentKind": 0, "WizardName": "Orders", - "SeededAt": "2026-08-26T10:15:00", "ListFormCode": "App.Mrp.Orders", "MenuCode": "App.Mrp.Orders", "MenuOrder": 1, @@ -1146,7 +1211,7 @@ varsayılan değeri olur ve **yeni kayıtta o alanı otomatik doldurur**. "SelectCommandType": 1, "SelectCommand": "Mrp_T_Order", "KeyFieldName": "Id", - "KeyFieldDbSourceType": 11, + "KeyFieldDbSourceType": 9, "Groups": [ { @@ -1155,7 +1220,7 @@ varsayılan değeri olur ve **yeni kayıtta o alanı otomatik doldurur**. { "FieldName": "Id", "CaptionName": "App.Listform.ListformField.Id", "Tr": "Kimlik", "En": "Id", - "EditorType": "dxTextBox", "DbSourceType": 11, + "EditorType": "dxTextBox", "DbSourceType": 9, "IsRequired": false, "IncludeInEditingForm": false, "ColSpan": 1 }, { @@ -1168,11 +1233,11 @@ varsayılan değeri olur ve **yeni kayıtta o alanı otomatik doldurur**. { "FieldName": "CustomerId", "CaptionName": "App.Listform.ListformField.Customer", "Tr": "Cari", "En": "Customer", - "EditorType": "dxSelectBox", "DbSourceType": 11, + "EditorType": "dxSelectBox", "DbSourceType": 9, "IsRequired": true, "IncludeInEditingForm": true, "ColSpan": 1, "LookupDataSourceType": 2, - "LookupQuery": "SELECT Id, Name FROM Crm_T_Customer WHERE IsDeleted = 0", - "ValueExpr": "Id", "DisplayExpr": "Name" + "LookupQuery": "SELECT Id AS [Key], Name AS [Name] FROM Crm_T_Customer WHERE IsDeleted = 0 ORDER BY Name", + "ValueExpr": "Key", "DisplayExpr": "Name" }, { "FieldName": "Quantity", "CaptionName": "App.Listform.ListformField.Quantity", @@ -1194,7 +1259,7 @@ varsayılan değeri olur ve **yeni kayıtta o alanı otomatik doldurur**. "EditorType": "dxNumberBox", "DbSourceType": 7, "IsRequired": false, "IncludeInEditingForm": true, "ColSpan": 2, "EditorOptions": "{\"readOnly\":true,\"format\":{\"type\":\"fixedPoint\",\"precision\":2}}", - "EditorScript": "// @builder {\"v\":1,\"rules\":[{\"id\":\"r1\",\"recipe\":\"multiply\",\"condition\":{\"operator\":\"always\",\"source\":\"\",\"value\":\"\"},\"params\":{\"left\":\"Quantity\",\"right\":\"UnitPrice\",\"target\":\"Total\",\"digits\":\"2\"}}]}\nset('Total', round(num('Quantity') * num('UnitPrice'), 2))" + "EditorScript": "// @builder {\"v\":1,\"rules\":[{\"id\":\"r1\",\"recipe\":\"multiply\",\"condition\":{\"operator\":\"always\",\"source\":\"\",\"value\":\"\"},\"params\":{\"left\":\"Quantity\",\"right\":\"UnitPrice\",\"target\":\"Total\",\"digits\":\"2\"}}]}\n// @runOnOpen\nset(\"Total\", round(num(\"Quantity\") * num(\"UnitPrice\"), 2))" } ] } @@ -1203,29 +1268,26 @@ varsayılan değeri olur ve **yeni kayıtta o alanı otomatik doldurur**. "WorkflowDto": { "ApprovalUserFieldName": "", "ApprovalDateFieldName": "", "ApprovalStatusFieldName": "", "ApprovalDescriptionFieldName": "", "ApprovalIsFilterUserName": false, "ApprovalIsResetWorkflow": false, "Criteria": [] } - }, - "IsDeletedField": true, - "IsCreatedField": true, - "InsertedRecords": { - "LanguageKeys": ["App.Mrp.Orders", "App.Mrp"], - "PermissionGroupNames": ["App.Mrp"], - "PermissionNames": ["App.Mrp.Orders", "App.Mrp.Orders.Create", "App.Mrp.Orders.Update", - "App.Mrp.Orders.Delete", "App.Mrp.Orders.Export", "App.Mrp.Orders.Import", - "App.Mrp.Orders.Note"], - "MenuCodes": ["App.Mrp", "App.Mrp.Orders"], - "DataSourceCodes": [] } } ``` -> `EditorScript` alanındaki `// @builder` başlığı ile onu izleyen kod satırı **birebir -> tutarlı** olmalıdır. Elle yazmak yerine C# tarafında `EditorScript.Build(...)` kullan ya da +Deploy sonrası dosyada `Wizard` bölümü `Menu`/`Language`/`Groups` başlıklarına toplanır ve +yalnızca `FieldName`/`CaptionName`/`En`/`Tr` kalır; ekranın kendisi `ListForm` + `Fields` +bölümlerine düşer (§4.1). `SeededAt` ilk deploy anında sunucu tarafından yazılır. + +> `EditorScript` alanındaki `// @builder` başlığı ile onu izleyen satırlar **birebir +> tutarlı** olmalıdır: açılışta çalışan bir kural varsa (hesap tarifleri varsayılan `both`) +> `// @runOnOpen` satırı gelir ve metin literalleri çift tırnaklıdır (`JSON.stringify`). Elle yazmak yerine C# tarafında `EditorScript.Build(...)` kullan ya da > Script Builder'da üret; uyuşmazsa dialog script'i "elle düzenlenmiş" sayar. -### 4.4.1 Seeder'ın senin yerine ürettikleri — bunları dosyaya yazma +### 4.4.1 Deploy'un ve seeder'ın senin yerine ürettikleri — cevaba yazma -`WizardDataSeeder`, dosyadaki `Wizard` bloğundan yola çıkarak aşağıdakileri **kendisi** üretir. -Bunları seed dosyasında aramaya kalkma, orada yokturlar: +Aşağıdakileri deploy wizard cevabından **kendisi** üretir (`ListFormWizardDto.BuildListForm` / +`BuildFields`; menü, yetki, dil ve veri kaynağı kayıtları `ListFormWizardAppService`). Ekrana ait +olanlar dosyanın `ListForm`/`Fields` bölümlerine düşer; `WizardDataSeeder` migrate'te ekranı +oradan olduğu gibi kurar (`GridOptionsEditDto.ApplyTo`), yeniden türetmez. Menü, yetki ve dil +kayıtlarını seeder `Wizard` bölümünden aynı kurallarla üretir: | Üretilen | Kural | | --- | --- | @@ -1240,7 +1302,7 @@ Bunları seed dosyasında aramaya kalkma, orada yokturlar: | `EditingFormJson` | `Groups` içinden; anahtar alan ve `IncludeInEditingForm: false` olanlar hariç | | Form genişlik/yükseklik | Grup kolon sayısı ve satır sayısından hesaplanır (en fazla 3 kolon) | | `FilterRowJson`, `HeaderFilterJson`, `SearchPanelJson`, `GroupPanelJson`, `ColumnOptionJson`, `PagerOptionJson`, `ExportJson` | Varsayılanlar | -| `DeleteCommand` | `IsDeletedField: true` ise soft delete `UPDATE` komutu; `false` ise `null` | +| `DeleteCommand` | `IsDeletedField: true` ise `WizardConsts.DefaultDeleteCommand(SelectCommand)` (soft delete `UPDATE`); `false` ise `null` | | `DefaultFilter` | `IsDeletedField: true` ise `"IsDeleted" = 'false'` | | `InsertFieldsDefaultValueJson` | `IsCreatedField: true` ise `CreationTime=@NOW`, `CreatorId=@USERID`, `IsDeleted=false`, `Id=@NEWID`; değilse yalnızca `Id=@NEWID` | | `DeleteFieldsDefaultValueJson` | `IsDeletedField: true` ise `DeleterId=@USERID`, `Id=@ID`; değilse yalnızca `Id=@ID` | @@ -1255,7 +1317,7 @@ Bunları seed dosyasında aramaya kalkma, orada yokturlar: - `Tree`/`Gantt` için `ParentIdExpr`, `Scheduler` için `TextExpr`, `Todo` için `TitleExpr` **ve** `StatusExpr` boşsa o görünümün JSON'u hiç yazılmaz — bayrağı `true` yapmak yetmez. - Anahtar alanı (`KeyFieldName`) `Groups` içine **her zaman** koyulur — ilk grubun ilk alanı - olarak, `IncludeInEditingForm: false` ile. Seeder onu kendiliğinden eklemez; yoksa listede + olarak, `IncludeInEditingForm: false` ile. Deploy onu kendiliğinden eklemez; yoksa listede anahtar kolonun `ListFormField` kaydı hiç oluşmaz ve satır anahtarı okunamadığı için düzenleme, silme, detay ve alt form ilişkileri çalışmaz. Yazdığında sütun otomatik gizlenir (`Visible = false`) ve forma girmez; tip bilgisi de oradan okunur. @@ -1266,8 +1328,8 @@ Bunları seed dosyasında aramaya kalkma, orada yokturlar: ### 4.5 Güncelleme, idempotency ve silme semantiği -**Seeder idempotenttir — bir ekran zaten varsa dosya atlanır.** "Zaten var" ölçütü yola göre -değişir: +**Seeder idempotenttir — bir ekran zaten varsa dosya atlanır** (yalnızca eksik dil metinleri +eklenir). "Zaten var" ölçütü yola göre değişir: | Yol | Kontrol | | --- | --- | @@ -1275,12 +1337,15 @@ değişir: | `Custom` + menülü | Bu `MenuCode` ile menü var mı | | `Custom` + menüsüz | Bu `MenuCode` adında yetki var mı | -Sonuç: **var olan bir ekranı seed dosyasını düzenleyerek değiştiremezsin.** Değiştirmenin üç -yolu vardır: +Sonuç: **kurulu bir ekranı seed dosyasını düzenleyerek değiştiremezsin** (dosya yalnızca boş +veritabanında etkilidir). Değiştirmenin yolları: -1. Wizard ekranından `EditFileName` ile yeniden çalıştır (önce eski dosya ve - `InsertedRecords` kayıtları silinir, sonra yenisi üretilir) — **tercih edilen yol**. -2. ListForm editöründen ince ayar yap (seed dosyasına yansımaz, §9.6 uyarısı). +1. **ListForm editörü** (`/admin/listform/edit/{kod}`) — alan ekleme, JSON kolonları, komutlar, + görünümler. Her kayıt `WizardSeedSynchronizer` ile aynı dosyanın `ListForm`/`Fields`/`Wizard` + bölümlerine geri yazılır; **ince ayar için tercih edilen yol** budur. +2. **`EditFileName` ile yeniden deploy** (wizard ekranı ya da MCP `replaceFileName`) — menü, + yetki grubu, veri kaynağı gibi wizard cevabına ait değişiklikler için. Ekran cevaptan yeniden + üretilir; editörde yapılmış, wizard'da karşılığı olmayan ayarlar kaybolur. 3. İlgili kayıtları elle sil, sonra seed'i yeniden çalıştır. > `List` yolunda seeder ekranı uygularken `ListForm`, `ListFormField` ve `ListFormWorkflow` @@ -1490,7 +1555,7 @@ kolonu vardır: | **Grid** | `ColumnOptionJson`, `RowJson`, `PagerOptionJson`, `SelectionJson`, `StateStoringJson` | — | | **Card** | `ColumnOptionJson` (kart alanları sütun tanımlarından türer) | — | | **Pivot** | `PivotOptionJson` + alan bazında `PivotSettingsJson` | — | -| **Chart** | `SeriesJson`, `LegendJson`, `ArgumentAxisJson`, `ValueAxisJson`, `TooltipJson`, `PanesJson`, `AnnotationsJson`, `CrosshairJson`, `ScrollBarJson`, `ZoomAndPanJson`, `SizeJson`, `MarginJson`, `TitleJson`, `AnimationJson`, `Common*Json` | `SeriesJson` | +| **Chart** | `SeriesJson`, `LegendJson`, `ArgumentAxisJson`, `ValueAxisJson`, `TooltipJson`, `PanesJson`, `AnnotationsJson`, `CrosshairJson`, `ScrollBarJson`, `ZoomAndPanJson`, `SizeJson`, `MarginJson`, `TitleJson`, `AnimationJson`, `Common*Json` | — (`SeriesJson` boşsa seri ekranda kurulur; grafik tarif edildiyse doldur, §9.8) | | **Tree** | `TreeOptionJson` | `KeyExpr` + `ParentIdExpr` | | **Gantt** | `GanttOptionJson` | `ParentIdExpr` + `TitleExpr` + `StartExpr` + `EndExpr` | | **Scheduler** | `SchedulerOptionJson` | `TextExpr` + `StartDateExpr` + `EndDateExpr` | @@ -1796,6 +1861,10 @@ bağlıdır. | integration | `location` | lat, lng, accuracy? | `await geoTo('Enlem', 'Boylam')` — cihaz konumunu alanlara yazar; HTTPS ve kullanıcı izni ister | | integration | `custom` | code | Satırın kendisi. Son çare; sondaki `;` atılır | +Tablodaki satırlar okunsun diye tek tırnakla yazıldı; üretilen metinde her literal +`JSON.stringify` ile **çift tırnaklıdır** (`set("Total", round(num("Quantity") * num("UnitPrice"), 2))`). +Elle yazarken de böyle yaz, yoksa gidiş-dönüş karşılaştırması tutmaz (§5.1). + `sum` ve `clear` çoklu alan alır (`fields` listesi); diğerlerinde her parametre tek alandır. Sayı bekleyen parametrelere sabit yazılabilir: `20` olduğu gibi, `KdvOrani` ise `num('KdvOrani')` olarak geçer. @@ -2099,8 +2168,9 @@ ListForm ile ifade edilemeyen ekranın yolu **fiziksel React sayfası değil**, ### 7.1 Seed dosyası `configs/seeds/{kapsam}/data/App.DeveloperKit.CustomComponents/{Id}.json` — Custom Components -ekranının veri dosyası. Bileşenin **kendi dosyası yoktur**; her bileşen bu dosyada bir satırdır -ve dosya `data/{ListFormCode}.json` sözleşmesini (§0.1) izler. +ekranının veri aynası, **klasör modunda** (§9.15): her bileşen kendi dosyasındadır, dosya adı +satırın `Id`'sidir. Dosya yine liste verisi zarfını taşır; `Rows` tek satırdır. Listeden ve Visual +Designer'dan (`CustomComponentAppService`) yapılan her kayıt bu klasöre yansır. ```jsonc { @@ -2128,15 +2198,24 @@ ve dosya `data/{ListFormCode}.json` sözleşmesini (§0.1) izler. "OperationType": "GetList", "SeedFile": "crud/Mrp_T_Order.json" } - ] + ], + "SeoTitle": null, "SeoDescription": null, "SeoKeywords": null, "SeoOgImage": null, + "SeoCanonicalUrl": null, "SeoNoIndex": true, "SeoIncludeInSitemap": false, + "SeoChangeFrequency": null, "SeoPriority": null, "SeoStructuredData": null } ] } ``` -- `Id` satırın anahtarıdır (`KeyFieldName`). Yeni bir bileşen yazarken üret; import ve seed - eşleşmesi bu değer üzerinden yapılır. -- Yeni bir bileşen dosyaya **satır olarak eklenir**; var olan satırlar korunur. +- `Id` satırın anahtarı ve dosyanın adıdır. Yeni bir bileşen yazarken sabit bir GUID üret; import + ve seed eşleşmesi bu değer üzerinden yapılır. Yeni bileşen = klasöre yeni dosya; diğer + dosyalara dokunulmaz. +- `Name` tekil ve `export default` adıyla aynıdır; Wizard `CustomComponentName` bu ada bakar. +- `Dependencies` bir **dizidir** (`[]`); eski satırlarda `""`/`"[]"` görülür, taklit etme. +- `Seo*` alanları her satırda bulunur (§0.9): `/admin/` rotalı ya da rotasız bileşende + `SeoNoIndex: true`, `SeoIncludeInSitemap: false`; public rotada doldurulur. Rotasız bileşende + `RoutePath` **`null`** yazılır, boş metin değil. +- Code modunda `Props` `null`'dır; Visual modda tasarımcı dokümanını taşır (§7.1.1). - `DataSources` elle yazılmaz: kaydetme sırasında `CustomComponentDataSourceResolver` tasarımcı dokümanındaki `dataSources` listesini `method + path` ile CRUD endpoint kataloğunda arayarak üretir. Eşleşenler `EntityName` + `SeedFile` taşır; elle yazılmış endpoint'ler listede @@ -2187,10 +2266,11 @@ ihtiyacın olan her şey kapsamdan gelir: | Kapsamdaki ad | Ne | | --- | --- | -| `React` | React'in tamamı (`React.useState`, `React.useEffect`…) | +| `React` | React'in tamamı; `useState`, `useEffect`, `useCallback`, `useMemo`, `useRef`, `useReducer`, `useContext`, `createContext`, `memo`, `Fragment` ayrıca çıplak ad olarak da gelir | | `components/ui` dışa aktarımlarının **tamamı** | `Button`, `Input`, `Select`, `Dialog`, `Card`, `Notification`, `toast`, … (yeni bir UI bileşeni eklendiğinde otomatik gelir) | | `UiKit` | Aynı kitin isim uzayı hâli | | `PlatformViewHost` | Bir ListForm ekranını gömmek için | +| Kanvas düğümleri (`componentEditor/runtimeNodes.ts`) | `PlatformIcon` (ikon — `react-icons` kapsamda **yoktur**), `BarcodeScanField`, `BarcodeViewer`, `Public*` site düğümleri | | `apiService` | Kimlik doğrulamalı HTTP istemcisi — **tercih edilen yol** | | `axios` | Ham HTTP (kimlik doğrulama eklemez) | | `DOMPurify` | HTML temizleme | @@ -2209,6 +2289,7 @@ Kurallar: - Yetkiyle gizlenecek her blok `checkPermission('App.…')` ile sarılır. Custom component route'u `authority: []` ile üretilir (§7.6 uyarısı) — kontrol bileşenin içindedir. - Başka bir custom component'i JSX olarak kullanabilirsin; adını `Dependencies` listesine ekle. +- `react-router` kapsamda yoktur; gezinme düz `` ya da `window.location` ile yapılır. ### 7.2 Tasarımcı dokümanı (`Props.visualDesigner`) @@ -2275,9 +2356,9 @@ olarak taşır (`?Name.contains=abc`). Bu, CrudEndpoint `GetList`'in okuduğu s | --- | --- | | `layout` | `PageContainer` (maxWidth, padding, gap, className), `FlexRow` (columns, firstColumnWidth, gap, wrap, align, className), `Table` | | `data` | `Form` (dört CRUD endpoint'ini sahiplenen kapsayıcı) + veri bağlanabilen bileşenler: `Select`, `AutoComplete`, `Dropdown`, `Menu`, `Pagination`, `Radio.Group`, `Tabs`, `Grid` | -| `platform` | `ListView`, `DataGridView`, `TreeView`, `GanttView`, `TodoBoard`, `CardView`, `SchedulerView`, `PivotView`, `ChartView` — her biri `listFormCode` ile bir ListForm ekranını gömer | -| `html` | `div`, `p`, `span`, `h1`–`h5`, `img` — yalnız bu dokuz etiket (`input`/`button`/`textarea`/`select`/`checkbox` kutuda yok, `ui` karşılıkları kullanılır) | -| `ui` | `components/ui` bileşenleri (`Spacer` dâhil) — sözleşmeleri `componentProps.json` metadata'sından okunur | +| `platform` | `ListView`, `DataGridView`, `TreeView`, `GanttView`, `TodoBoard`, `CardView`, `SchedulerView`, `PivotView`, `ChartView` — her biri `listFormCode` ile bir ListForm ekranını gömer. Aynı ailede public site düğümleri: `PublicHeaderBar`, `PublicNav`, `PublicMobileMenu`, `PublicLogo`, `PublicThemeToggle`, `PublicLanguageSelector`, `PublicDemoButton` | +| `html` | `a`, `div`, `p`, `span`, `h1`–`h5`, `img` — yalnız bu on etiket (`input`/`button`/`textarea`/`select`/`checkbox` kutuda yok, `ui` karşılıkları kullanılır) | +| `ui` | `components/ui` bileşenleri — sözleşmeleri `componentProps.json` metadata'sından okunur — ve `catalog.ts`'teki elle tanımlı ekler: `PlatformIcon`, `Spacer`, `BarcodeScanField` (kamerayla okut, `onScan`), `BarcodeViewer` (`value`, `format` — varsayılan `QRCode`; `Code128`, `EAN13`, `DataMatrix`…, `label`, `showText`, `scale`, `fitWidth`) | | `custom` | Diğer custom component'ler | **Önce yeniden kullan:** istenen şey zaten bir ListForm ekranının verdiği liste/ağaç/grafikse, @@ -2296,6 +2377,7 @@ onu primitiflerden yeniden kurma — ilgili `platform` düğümünü `listFormCo > | --- | --- | > | `components/ui/{Name}` olayı/prop'u zaten taşıyor, panel göstermiyor | Olaysa: bileşen `DESIGNER_PRIMARY_EVENTS` kaydındaysa panel **yalnız o dizideki** olayları gösterir → `catalog.ts`'te diziye olay adını ekle (isteğe bağlı `DESIGNER_EVENT_SNIPPETS` başlangıç script'i). Prop'sa önce filtrelere bak: `HIDDEN_GENERATED_PROPERTIES` (`field`, `form`) ve `catalog.ts`'teki bileşene özel elemeler (`Select.items`, `Menu.variant`, `Table.columns/data`, `Checkbox.defaultChecked`); filtrede değilse `componentProps.json` girdisi eksiktir, tamamla | > | `components/ui/{Name}` olayı/prop'u hiç taşımıyor | Aynı bileşene `on*` prop'unu ya da yeni prop'u ekle (varsayılanı mevcut davranışı koruyacak şekilde) → `generated/componentProps.json`'daki bileşen girdisine **elle** ekle (`type: "function"` olay için; dosya otomatik üretilmez, README) → olaysa adını `DESIGNER_PRIMARY_EVENTS`'e; değer kümesi metadata'da tip adıyla geliyorsa `NAMED_UNION_OPTIONS` (anahtar **tsType**), tek bileşene özel varsayılan/seçenek gerekiyorsa `PROPERTY_OVERRIDES` (anahtar **bileşen → prop**). Kod üreticisi (`codeGenerator.ts`) prop'u JSX niteliği olarak basar, olay için `handle_{düğüm}_{olay}` handler'ını `node.events`'ten kendisi üretir | +> | Bileşen `catalog.ts`'teki elle tanımlı eklerden (`DESIGNER_EXTRAS`: `PlatformIcon`, `BarcodeViewer`…) | Prop/olay sözleşmesi `componentProps.json`'da değil, girdinin kendisindedir; oraya ekle. Kanvasın doğrudan çizdiği düğümse `componentEditor/runtimeNodes.ts` sözlüğü de güncellenir | > | Script Builder'da da kullanılacak | `designerScriptDialect.ts` / `designerScriptRecipes.ts`'te olay `cancelable` mı, hangi tarifler listelenecek — ekle; `ref` API'sine yeni metot eklediysen §7.8 tablosu | > | Sonrasında | `npm run typecheck` + `npm run lint`; `lowcode-reference.instructions.md` §10.9 prop tablosu / §10.10 olay tablosu güncellenir | > @@ -2492,7 +2574,9 @@ Bileşen tek başına bir ekran değildir; menü ve yetki Wizard'ın `Custom` yo } ``` -Custom yolunda `ListForm` **üretilmez**; `Title`/`Desc` dil anahtarları da üretilmez. +Yukarıdaki düz cevap deploy girdisidir (§4.4); dosyada `Menu`/`Language` başlıklarına toplanır, +`ListForm: null`, `Fields: []` yazılır. Custom yolunda `ListForm` **üretilmez**; `Title`/`Desc` +dil anahtarları da üretilmez. `Export`/`Import`/`Note` yetkileri bu yolda yoktur. > **Güvenlik — route yetkisiz üretilir.** Custom component route'ları `RoutePath` değerinden @@ -2505,44 +2589,28 @@ Custom yolunda `ListForm` **üretilmez**; `Title`/`Desc` dil anahtarları da ür > > `RoutePath` `/admin/` ile başlamıyorsa sayfa **herkese açıktır** — bilinçli değilse başlat. -### 7.6.1 Depodaki gerçek örnek — `RoleComponent` + `RoleList` wizard +### 7.6.1 Depodaki örnek — `RoleComponent` + Custom wizard bağı -Bu çift, custom component'in menüye nasıl bağlandığının çalışan örneğidir. İncelenecek dosyalar: +`data/App.DeveloperKit.CustomComponents/932daf62-….json` (`RoleComponent`, Visual mod) ile +`crud/AbpRoles.json` · `AbpUsers.json` · `AbpUserRoles.json` custom component tarafının çalışan +örneğidir: `Form` + `Input`/`Checkbox` düğümleri CRUD data source'larına bağlıdır, altta +`ListView` düğümü var olan `AbpIdentity.Roles` ekranını gömer. Onu menüye bağlayan wizard dosyası +(`RoleList.json`) depodan silinmiştir; bileşen şu an menüsüz, yalnızca `RoutePath` ile açılır ve +`permissionCode` boştur — **yetki kalıbı için örnek alma**. Bağ kurulurken şu değerler eşleşir: -``` -configs/seeds/host/data/App.DeveloperKit.CustomComponents/{Id}.json ← RoleComponent satırı -configs/seeds/host/crud/AbpRoles.json · AbpUsers.json · AbpUserRoles.json -configs/seeds/host/wizard/RoleList.json -``` - -Nasıl bağlanıyorlar: - -| Bağ | Değer | +| Bağ | Kural | | --- | --- | | Wizard `ComponentKind` | `1` (Custom) | -| Wizard `CustomComponentName` | `RoleComponent` | -| Component `RoutePath` | `/admin/roles` | -| Wizard `MenuUrl` | `/admin/roles` — component'in `RoutePath` değeriyle **aynı** | -| Wizard `MenuCode` / `ListFormCode` | `App.Wizard.RoleList` | -| Component `Props.visualDesigner.permissionCode` | `App.Wizard.RoleList` — wizard'ın ürettiği okuma yetkisi | -| Wizard `InsertedRecords.PermissionNames` | `App.Wizard.RoleList` + `.Create` + `.Update` + `.Delete` | -| Wizard `InsertedRecords.LanguageKeys` | `App.Wizard.RoleList` | -| Component `DataSources[].SeedFile` | `crud/AbpRoles.json` vb. | +| Wizard `CustomComponentName` | Bileşen satırının `Name`'i (`RoleComponent`) | +| Wizard `MenuUrl` | Bileşenin `RoutePath`'i — **birebir aynı**; farklıysa menü başka yere gider | +| Wizard `MenuCode` / `ListFormCode` | Yetkilerin kökü (`App.Wizard.RoleList`) | +| Bileşen `Props.visualDesigner.permissionCode` | Wizard'ın ürettiği okuma yetkisi = `MenuCode`; Form komutları `Otomatik` modda buna `.Create`/`.Update`/`.Delete` ekler | +| Wizard `InsertedRecords.PermissionNames` | kök + `.Create` + `.Update` + `.Delete` | +| Bileşen `DataSources[].SeedFile` | `crud/AbpRoles.json` vb. — export zip'inin bu dosyaları da toplamasını sağlar | -**Okunacak dersler:** - -1. `permissionCode` **wizard'ın ürettiği yetkinin aynısıdır**. Form komutları `Otomatik` modda - bu koda `.Create`/`.Update`/`.Delete` ekleyerek çözülür. Component'i yazarken bu değeri - wizard'daki `MenuCode` ile aynı yap. -2. `RoutePath` ile wizard'ın `MenuUrl` değeri **birebir aynı** olmalıdır; farklıysa menü başka - bir yere gider. -3. Component'in `DataSources` listesi `crud/*.json` dosyalarına `SeedFile` ile işaret eder — - export zip'inin bu dosyaları da toplamasını sağlayan şey budur. -4. Wizard `Custom` yolunda olsa bile `ListFormCode`, `Grid/Card/Pivot/Chart` bayrakları gibi - List alanlarını taşır; **bunlar kullanılmaz**, varsayılan değerleriyle dosyada kalır. - Yeni dosya yazarken bunları temizlemeye çalışma, şablonu koru. -5. `platform` düğümündeki `listFormCode` (`AbpIdentity.Roles`) **var olan bir ListForm kodudur**; - component ekranın kendisini yeniden kurmaz, gömer. +Ek dersler: Custom wizard dosyası List alanlarını (`Grid`/`Card`/…, `SelectCommand`) varsayılan +değerleriyle taşır, kullanılmaz (§4.1); `platform` düğümündeki `listFormCode` **var olan** bir +ekran kodudur — bileşen ekranı yeniden kurmaz, gömer. ### 7.7 Taşıma @@ -2776,9 +2844,9 @@ seed dosyası yazarken aynı setleri kullan: | Multi-Tenant | `TenantId` (`uniqueidentifier`, null) | | Full Audited | `Id` (`uniqueidentifier`, PK, `NEWID()`), `CreationTime` (`datetime2`, `GETUTCDATE()`), `CreatorId`, `LastModificationTime`, `LastModifierId`, `IsDeleted` (`bit`, `0`), `DeletionTime`, `DeleterId` | | Workflow | `UserName` (`nvarchar(256)`), `Status` (`nvarchar(50)`), `Date` (`datetime`), `Description` (`nvarchar(200)`) — `WorkflowDto`'nun dört alan adının karşılığı | -| Todo | `Title` (`nvarchar(300)`), `Status` (`nvarchar(50)`, `N'Backlog'`), açıklama/öncelik/sıra kolonları — Kanban ekranının veri tabanı | -| Gantt | `Start` / `End` / `Progress` kolonları | -| Scheduler | Randevu başlangıç/bitiş/tekrar kolonları | +| Todo | `Title` (`nvarchar(300)`), `Status` (`nvarchar(50)`, `N'Backlog'`), `Description`, `DueDate`, `Tags`, `Assignees`, `Priority`, `Completed` (`bit`, `0`), `SortOrder` (`int`, `0`), `Timesheets`, `SubTasks` — Kanban ekranının veri tabanı | +| Gantt | `Title`, `ParentId` (`uniqueidentifier`, null), `StartDate`, `EndDate` (`datetime2`), `Progress` (`int`, `0`) | +| Scheduler | `UserName`, `Text`, `Description`, `StartDate`, `EndDate`, `AllDay` (`bit`, `0`), `RecurrenceRule`, `RecurrenceException` | Yani "onaylı talep formu" ya da "kanban panosu" isteğinde tablo kolonlarını uydurma: ilgili şablon setini Full Audited + Multi-Tenant ile birleştir. @@ -2951,8 +3019,8 @@ içindir. ## 9. Artefakt F — CRUD Endpoint ve Developer Kit Bu bölüm CRUD Endpoint (9.1), elle yazılmış SQL'i API'ye çeviren Custom Endpoint (9.2), runtime -derlenen Dynamic Service (9.3), Developer Kit ekranları (9.4–9.8) ve platformun hazır gelen -konfigürasyon ekranlarını (9.9–9.14) kapsar. +derlenen Dynamic Service (9.3), Developer Kit ekranları (9.4–9.8), platformun hazır gelen +konfigürasyon ekranlarını (9.9–9.14) ve liste verisi seed dosyasını (9.15) kapsar. ### 9.1 CRUD Endpoint seed dosyası @@ -3073,11 +3141,10 @@ Endpoint **tanımlarını** düzenleme yetkileri ListForm ekranının `App.DeveloperKit.CustomEndpoints{,.Create,.Update,.Delete}` yetkileridir — çağırma yetkilerinden (`.Get/.Post/.Put/.Remove` + `PermissionsJson`) ayrıdır. -**Seed:** platform varsayılanları `configs/seeds/{kapsam}/data/App.DeveloperKit.CustomEndpoints.json` -dosyasından gelir; ekranın `SeedSyncInsert/Update/Delete` bayrakları **kapalıdır**, yani ekrandan -girilen kayıt dosyaya yansımaz ve yalnızca veritabanında yaşar. Proje teslimi için kaydı ya bu -dosyaya elle ekle ya da `sql/execute/{Ad}.sql` upsert prosedürüyle geri getirilebilir yap -(§9.14 uyarısı). +**Seed:** `configs/seeds/{kapsam}/data/App.DeveloperKit.CustomEndpoints.json` (§9.15). Ekranın +`SeedSyncInsert/Update/Delete` bayrakları **açıktır**: ekrandan eklenen/değiştirilen/silinen kayıt +dosyaya yansır, migrate'te eksikse geri eklenir. Prompt ile üretimde satırı bu dosyaya yaz +(`Id` sabit GUID; `ParametersJson`/`PermissionsJson` kaçışlanmış string). **Ne zaman CRUD, ne zaman Custom Endpoint?** Tek tabloya standart CRUD gerekiyorsa CRUD Endpoint (otomatik üretilir, `crud/{Entity}.json` ile seed'lenir, filtre sözleşmesi hazırdır). @@ -3171,15 +3238,16 @@ TestCompile ──► Publish ──► kuyruğa kayıt ──► assembly tenan bilgisiyle `LastCompilationError`'a ve UI'a düşer. Yönetim yetkileri: -`App.DeveloperKit.DynamicServices{,.Create,.Edit,.Delete,.Manage,.TestCompile,.Publish,.ViewCode}` — +`App.DeveloperKit.DynamicServices{,.Create,.Update,.Delete,.Manage,.TestCompile,.Publish,.ViewCode}` — `Manage` aktif/pasif ve toplu işlemler, `ViewCode` kaynak görüntüleme kapısıdır. Servisin **kendi uçlarının** yetkisi ise koddaki `[Authorize(...)]` özniteliğidir; sınıfı yetkisiz bırakma, gerekiyorsa `data/Abp.Identity.Permissions.json`'a yeni yetki tanımla (CLAUDE.md kesişen değişiklik kuralı). -**Seed:** `DynamicService` kayıtları yalnızca veritabanında yaşar; hiçbir seed dosyası okunmaz. -Veritabanı sıfırlanınca geri gelmesi gereken bir servis için `sql/execute/{Ad}.sql` upsert -prosedürü yaz (§9.14) — `Code` alanını string literal olarak taşır. +**Seed:** `data/App.DeveloperKit.DynamicServices.json` (§9.15; seed bayrakları açık — dosya ilk +kayıtta oluşur). Veritabanı sıfırlanınca kayıt bu dosyadan döner; açılışta yalnızca +`IsActive && CompilationStatus == Success` olanlar derlendiği için dosyadaki satır bu iki değeri +taşımalıdır. `Code` tam C# kaynağıdır, JSON string olarak kaçışlanır. Yazarken `dotnet.instructions.md` §5 geçerlidir: tenant bağlamını `ICurrentTenant` üzerinden kullan, SQL'i parametreli çalıştır, kullanıcıya görünen metni dil anahtarından ver, @@ -3232,7 +3300,8 @@ bağlıdır; hiçbiri "kod yaz" adımının yerine geçmez, hepsi ondan öncedir | **Dynamic Services** | `/admin/list/App.DeveloperKit.DynamicServices` | `App.DeveloperKit.DynamicServices` | Runtime derlenen C# AppService | 9.3 | | **Components** | `/admin/list/App.DeveloperKit.CustomComponents` | `App.DeveloperKit.CustomComponents` | Custom Component + Visual Designer | 7 | | **ListForm** | `/admin/list/App.Listforms.Listform` | `App.Listforms.Listform` | Var olan ekranların ham tanımı | 9.6 | -| **Wizard Manager** | `/admin/listform/wizardManager` | `App.Listforms.Wizard` | Wizard seed dosyalarının yönetimi, export/import | 4, 7.7 | +| **Wizard Manager** | `/admin/listform/wizardManager` | `App.Listforms.Wizard` | Wizard seed dosyalarının yönetimi (menü yolu, bağımlılık ağacı, silme), export/import | 4, 7.7 | +| **MCP Servers** | `/admin/list/App.DeveloperKit.McpServers` | `App.DeveloperKit.McpServers` (+ `.Invoke`, `.Write`, `.Develop`) | Dış yapay zekâ istemcisinin `/mcp` erişimi ve açık araçlar (`AllowedTools`) | 0.10 | ### 9.5 SQL Query Manager — bileşen bileşen @@ -3306,7 +3375,7 @@ Her sekmenin düzenlediği kolonlar — alan alan sözleşmeleri `lowcode-refere | **Alt Form** | `SubFormsJson` | ref §5.2 | | **Widget** | `WidgetsJson` | ref §5.3 | | **Workflow** | `WorkflowJson` + `ListFormWorkflow` düğümleri | §4.7 | -| **Grafik sekmeleri** (Ortak, Eksen, Bölme, Seri, Animasyon, Açıklama, Zoom, Legend, Crosshair, Export) | 22 grafik JSON kolonu | ref §8 | +| **Grafik sekmeleri** (Ortak, Eksen, Bölme, Seri, Animasyon, Açıklama, Zoom, Legend, Crosshair, Export) | 21 grafik JSON kolonu | ref §8 | **Veritabanı alt sekmeleri:** @@ -3342,9 +3411,11 @@ taşır — son çaredir, önce `EditorScript`/`EditorOptions` denenmelidir. Editörün üstündeki dil/rol/kullanıcı seçicileri **varyant** düzenler (ref §5.7): seçim yapınca aynı ekranın o bağlama özgü kopyası üzerinde çalışırsın. -> **Uyarı:** Bu ekrandan yapılan değişiklikler doğrudan veritabanına yazılır ve wizard seed -> dosyasına **yansımaz**. Kalıcı olması gereken bir değişikliği ya wizard'ı `EditFileName` ile -> yeniden çalıştırarak ya da seed dosyasını güncelleyerek yap. Prompt ile üretimde bu editör +> **Seed'e yansıma:** Bu ekrandan yapılan her kayıt (liste ayarları, alanlar, JSON satır +> editörleri — `ListFormsAppService`, `ListFormFieldsAppService`, `ListFormJsonRowAppService`) +> ekranın wizard seed dosyasına `WizardSeedSynchronizer` ile geri yazılır; wizard'dan üretilmemiş +> (C# seeder'la gelen platform) ekranlarda karşılık gelen dosya yoktur ve değişiklik yalnızca +> veritabanında kalır. Prompt ile üretimde bu editör > senin "hangi anahtar nerede" haritandır: kullanıcı "ekranda şunu değiştir" dediğinde önce bu > tablodan hangi JSON kolonuna dokunacağını bul, sonra seed dosyasında o kolonu güncelle. @@ -3380,8 +3451,9 @@ elle düzeltme ve wizard'ın kapsamadığı düzenlemeler içindir. **Kurallar — prompt ile üretim yaparken:** 1. **Menüyü elle ekleme.** Yeni bir ekranın menüsü wizard seed dosyasından gelir; - `data/App.Menus.Menu.json` yalnızca platformun kendi ekranları içindir ve - `App.Menus.Routes.json` dosyasına hiç dokunulmaz. + `data/App.Menus.Menu.json` (anahtar `Code`) platformun kendi ekranları ve `Pub` modülü + (public site, §0.9) içindir. `App.Menus.Routes.json`'da yalnızca var olan satırın `Seo*` + alanlarına dokunulur; rota eklemek/silmek/yolunu değiştirmek çerçeve dışıdır (§0.8). 2. **Route kaydı üretme.** Dinamik liste ekranları statik `/admin/list/:listFormCode` route'unu kullanır; custom component'lerin route'u `RoutePath` alanından türetilir. `Route` tablosu yalnızca fiziksel React sayfaları içindir ve prompt ile ekran üretirken böyle bir sayfa @@ -3402,10 +3474,10 @@ saklanır. | Görünüm | Bayrak | `DefaultLayout` | Zorunlu option alanı | Ne zaman aç | | --- | --- | --- | --- | --- | -| Grid | `Grid` | `"grid"` | — | Neredeyse her zaman; varsayılan | -| Card | `Card` | `"card"` | — | Görsel/özet kayıtlar, mobil kullanım | -| Pivot | `Pivot` | `"pivot"` | — | Sayısal kırılım/analiz ihtiyacı | -| Chart | `Chart` | `"chart"` | — (grafik ayarları ListForm editöründe) | Trend/dağılım | +| Grid | `Grid` | `"grid"` | — | **Varsayılan açık**; açılış görünümü | +| Card | `Card` | `"card"` | — | **Varsayılan açık** | +| Pivot | `Pivot` | `"pivot"` | — (alanın `PivotSettingsJson.IsPivot`) | **Varsayılan açık** | +| Chart | `Chart` | `"chart"` | — (`SeriesJson` boşsa kullanıcı seriyi ekranda kurar) | **Varsayılan açık** | | Tree | `Tree` | `"tree"` | `TreeOptionDto.KeyExpr` + `ParentIdExpr` | Kendine referanslı hiyerarşi | | Gantt | `Gantt` | `"gantt"` | `GanttOptionDto.ParentIdExpr` + `TitleExpr` + `StartExpr` + `EndExpr` | Zaman çizelgesi, bağımlılıklı görevler | | Scheduler | `Scheduler` | `"scheduler"` | `SchedulerOptionDto.TextExpr` + `StartDateExpr` + `EndDateExpr` | Takvim/randevu | @@ -3413,6 +3485,15 @@ saklanır. Kurallar: +- **Varsayılan dörtlü:** prompt, görsel ya da tarifle üretilen her liste ekranı (Wizard `List` yolu, + MCP `deploy_wizard`, alt form olarak kullanılan liste dahil) `Grid`, `Card`, `Pivot` ve `Chart` + bayrakları `true` ve `DefaultLayout: "grid"` ile yazılır. Bir görünüm yalnızca istek onu açıkça + dışarıda bıraktığında ("pivot olmasın", "sadece liste") `false` yazılır; "liste/tablo/grid" demek + diğer üçünü kapatmak değildir. Tree/Gantt/Scheduler/Todo varsayılan kapalıdır, zorunlu alanları + olduğu için yalnızca istenince açılır. Kod tarafı aynı varsayılanı taşır: UI wizard başlangıç + değerleri, `ListFormWizardDto` ve `LayoutDto` — bayrak yazılmayan cevap dörtlüyü açık alır. +- Prompt bir grafik tarif ediyorsa ("aylara göre ciro") `SeriesJson` da doldurulur; tarif yoksa + Chart açık kalır, seri kullanıcı tarafından ekranda kurulur. - Zorunlu option alanı boşsa seeder o görünümün JSON'unu **yazmaz**; bayrak açık kalsa bile görünüm çalışmaz. Açtığın her görünümün option bloğunu doldur. - `DefaultLayout`, bayrağı `true` olan bir görünüm olmalıdır. @@ -3470,6 +3551,9 @@ Ekran: `/admin/list/App.Definitions.Sequence` · Yetki: `App.Definitions.Sequenc Örnek — `SIP-2026-00042`: `Prefix = "SIP-"`, `PaddingSize = 5`, `ResetPeriod = 1` (Yearly), `FormatTemplate = "{Prefix}{yyyy}-{Number}"`. +**Seed:** `data/App.Definitions.Sequence.json` (seed bayrakları kapalı — yeni numaratörü satır +olarak bu dosyaya yaz; `NextNumber` başlangıç değeridir, `LastPeriodKey` yazılmaz). + Numara **yazma anında** (Insert) alınır ve `NextNumber` ilerler; form açılışında yalnızca önizleme istenir. Bu yüzden iptal edilen bir kayıt numarayı tüketebilir — boşluksuz numara gerekiyorsa numarayı `InsertAfterCommand` ile SQL tarafında üret. @@ -3485,6 +3569,7 @@ Ekranlar: `App.BackgroundWorkers.RecurringJobs` (tanımlar) ve `App.BackgroundWo | `Name` | Benzersiz ad; dağıtık kilit (`DistributedLock`) bu adla alınır — aynı iş iki sunucuda aynı anda çalışmaz | | `Cron` | Cron ifadesi (`*/5 * * * *`, `0 3 * * *`) | | `WorkerType` | `1` MailQueueWorker · `2` SqlWorker · `3` NotificationWorker · `4` SessionCleanupWorker · `5` BackupWorker | +| `Scope` | `1` Host (varsayılan) · `2` Tenant · `3` All — aşağıda **Kapsam** | | `DataSourceCode` | `BeforeSp` / `AfterSp` hangi bağlantıda çalışacak | | `BeforeSp` / `AfterSp` | İş gövdesinden **önce ve sonra** çağrılan stored procedure adları | | `Options` | Tipe özel JSON (MailQueueWorker için kuyruk ayarları) | @@ -3517,8 +3602,8 @@ Her tetiklemede çalışma sırası: `Status`, `FailureReason`, `ExceptionDetails`) ve her satır tek bir kapsamın çalışmasıdır. **Kapsam:** `Scope` alanı işin gövdesinin hangi bağlamda çalışacağını belirler — -`Host` (1) yalnızca host, `Tenant` (2) her kiracı için bir kez, `Host ve Kiracılar` (3) önce host -sonra kiracılar — her biri ayrı bir iş kaydı üretir, Jobs ekranındaki `Tenant` kolonu hangi +`Host` (1) yalnızca host, `Tenant` (2) her kiracı için bir kez, `All` (3, ekranda "Host ve +Kiracılar") önce host sonra kiracılar — her biri ayrı bir iş kaydı üretir, Jobs ekranındaki `Tenant` kolonu hangi kiracıda çalıştığını gösterir. Kiracı tablosuna dokunan bir iş `Host` kapsamında çalışırsa kiracı satırlarını göremez; kiracı verisi işleyen her işe kapsamını ver. @@ -3529,6 +3614,10 @@ prosedürlerindedir. Yani "her gece stok kapanışı" demek = `WorkerType: 2` + Tanımlar değiştikten sonra zamanlayıcıya yansıması için **"Zamanlanmış işleri yeniden üret"** çalıştırılır: toolbar butonu `UiEvalService.ApiGenerateBackgroundWorkers()` (§9.13). +**Seed:** `data/App.BackgroundWorkers.RecurringJobs.json` (§9.15; seed bayrakları açık — ekrandan +yapılan kayıt dosyaya yansır). Prompt ile iş eklerken satırı bu dosyaya yaz; gövde prosedürü +ayrıca `sql/object/` altında durur. + **Otomasyon reçetesi** — "belirli zamanda şu iş yapılsın" taleplerinin tamamı bu üçlüyle kurulur, kod yazılmaz: @@ -3558,30 +3647,39 @@ JSON'udur: ### 9.11 Bildirimler (`App.Notifications.*`) -Dört ekran, tamamı **Administration › Bildirimler** altındadır: **Notification Types** (bildirim -türü tanımı), **Notification Rules** (kim, hangi türde, hangi kanaldan alacak), **Notifications** -(üretilmiş bildirimler / geçmiş **ve** elle gönderim formu), **Notification Templates** (mesaj -şablonları). Üç tablo da kiracı kapsamındadır (`Adm_T_NotificationType`, -`Adm_T_NotificationRule`, `Adm_T_Notification`); yeni kiracıya varsayılan tür ve kural kümesi -kopyalanır (`README.md` §13.5.1). +Dört ekran, tamamı **Administration › Bildirimler** altındadır: **NotificationTemplates** (mesaj +şablonları), **NotificationTypes** (olay türü), **NotificationRules** (kim, hangi türde, hangi +kanaldan alacak), **Notification** (üretilmiş bildirimler / geçmiş **ve** elle gönderim formu — +kod tekildir: `App.Notifications.Notification`). Dört tablo da kiracı kapsamındadır +(`Adm_T_NotificationTemplate`, `Adm_T_NotificationType`, `Adm_T_NotificationRule`, +`Adm_T_Notification`); yeni kiracıya host seed dosyalarındaki tür ve kural kümesi yeni kimliklerle +kopyalanır (`NotificationTenantDefaultsSeedContributor`, `README.md` §13.5.1). | `NotificationRule` alanı | Anlam | | --- | --- | -| `NotificationTypeId` | Hangi tür olay (`YeniSiparis`, `SiparisOnaylandi`…) | -| `RecipientType` + `RecipientId` | `User` / `Role` / `OrganizationUnit` + kullanıcı adı, rol adı ya da birim kodu | -| `Channel` | `Mail`, `Sms`, `WhatsApp`, `Rocket`, `Desktop`, `UiToast`, `UiActivity` | +| `NotificationTypeId` | Hangi tür olay (`YeniSiparis`, `YeniKullanici`…) | +| `RecipientType` + `RecipientId` | `User` / `Role` / `OrganizationUnit` / `All` / `Custom` + kullanıcı adı, rol adı ya da birim kodu | +| `Channel` | `Mail`, `Sms`, `WhatsApp`, `Telegram`, `Rocket`, `Desktop`, `UiToast`, `UiActivity` — ekran etiketi `App.NotificationChannel.{Kanal}` (eski `App.StaticLookup.*` kanal anahtarlarını kullanma) | | `IsActive` | Kural açık mı | | `IsFixed` | Kullanıcı profilinden kapatılamaz (zorunlu bildirim) | -| `IsCustomized` | Kullanıcı kendi tercihini yazmış | +| `IsCustomized` | Kullanıcı kendi tercihini yazmış (yalnız sahibini kapatır, alıcıyı kurallar belirler) | -Bildirim üretme yolu: bir `Notification` kaydı oluşur (tür, kural, alıcı, mesaj) ve -`NotificationWorker` (§9.10) kuyruğu kanala göre gönderir. Kayıt SQL tarafından -(`InsertAfterCommand`, prosedür) ya da Dynamic Service'ten yazılabilir. +Bildirim üretme yolu: bir `Notification` kaydı oluşur (tür, kural, kanal, alıcı `UserId`, +`Subject`, `Message`) ve `NotificationWorker` (§9.10, `Scope: 3`) kuyruğu kanala göre gönderir; +kayıt anında `/notificationhub` üzerinden de duyurulur. Kayıt SQL tarafından (`InsertAfterCommand`, +prosedür) ya da Dynamic Service'ten yazılabilir. Bayrak sırası: `IsSent` olmadan `IsRead` +yazılmaz, `IsRead` ile `ReadTime` birlikte yazılır. -Kural yazmadan gönderim istendiğinde ayrı bir ekran/tablo açılmaz: **Notifications** ekranının -ekleme formu alıcı (`UserId`) ve kanal (`NotificationChannel`) çoklu seçimini alır, -`list-form-dynamic-api/notification-insert` ucu her alıcı × kanal açılımı için bir satır yazar. -Bildirim tipi `Manuel`'dir; kuyruğu yine `NotificationWorker` boşaltır. +Kural yazmadan gönderim istendiğinde ayrı bir ekran/tablo açılmaz: **Notification** ekranının +ekleme formu `list-form-dynamic-api/notification-insert` ucuna bağlıdır (`CreateNotificationInput`, +yetki `App.Notifications.Notification.Create`). Form alıcıyı (`UserId`) ve kanalı +(`NotificationChannel`) çoklu seçer (`dxTagBox`, §4.3.2.1), şablon (`NotificationTemplateId`) +seçilirse boş bırakılan `Subject`/`Message` şablondan dolar; mesajda `{AdSoyad}`, `{KullaniciAdi}`, +`{Eposta}`, `{Tarih}`, `{Saat}`, `{Kiraci}` yer tutucuları çözülür. Uç her alıcı × kanal için bir +satır yazar, bildirim tipi `Manuel`'dir. + +**Seed:** `data/App.Notifications.{NotificationTemplates,NotificationTypes,NotificationRules}.json` +(§9.15; seed bayrakları açık, `Order` ile tür → kural sırası). Üretilmiş bildirimler seed'lenmez. Onay akışındaki **`Inform` düğümü e-posta gönderir** (MailQueue üzerinden); UI bildirimi istiyorsan ayrıca bir bildirim kuralı gerekir. @@ -3603,7 +3701,8 @@ Route'lar: görüntüleyici `/admin/reports/{report}/view/{id?}/{listFormCode?}` - Kendi düzeni istenen çıktı (fatura, teklif, iş emri) için `ReportTemplate` kaydı açılır ve tasarımcıdan çizilir; ekrana `CommandColumnJson` butonu ile bağlanır (`Url: "/admin/reports/{ReportAdi}/view/@Id"`). -- Rapor şablonları veritabanı kaydıdır; `configs/seeds/` altına düşmez (§9.14). +- Rapor şablonları ve kategorileri `data/App.Reports.{ReportTemplates,Categories}.json` ile + seed'lenir; seed bayrakları kapalıdır, tasarımcıda yapılan değişiklik dosyaya yansımaz (§9.14). ### 9.13 `UiEvalService` — buton ve script'ten çağrılabilen platform aksiyonları @@ -3646,30 +3745,32 @@ kullanılacağı** `lowcode-reference.instructions.md` §12'dedir. | "Genel siteye sayfa" | Custom Component (`Pub` modülü) — Visual Designer'dan yeni component + `Pub` menü kaydı | `/admin/developerkit/components/design/:id` (README.md §10.4) | | "Yapay zekâ asistanı" | AI Bot tanımları | `/admin/list/App.Definitions.AiBot` | -> **Seed uyarısı — bu kayıtlar `configs/seeds/` altına düşmez.** Wizard, custom component, CRUD -> endpoint ve SQL nesneleri seed dosyası üretir; Sequence, BackgroundWorker, NotificationRule, -> ReportTemplate, GlobalSearch gibi kayıtlar **yalnızca veritabanında** yaşar. Veritabanı sıfırlanınca -> geri gelmesi gerekiyorsa iki yol vardır: +> **Seed durumu — hangi kayıt dosyaya düşer?** Bir kayıt ekranının `ListForm.SeedFilePath`'i +> doluysa veritabanı sıfırlandığında `data/` dosyasından döner (§9.15); değilse yalnızca +> veritabanında yaşar. Üç durum vardır: > -> 1. `sql/execute/{Ad}.sql` altında, kaydı **varsa güncelleyen/yoksa ekleyen** bir prosedür yaz -> (§8.4 sözleşmesi) — proje kapsamındaki doğru yol budur. -> 2. Platformun kendi varsayılanıysa ilgili ekranın `configs/seeds/host/data/{ListFormCode}.json` -> dosyası (`App.Definitions.AiBot`, `App.SettingDefinitions`, `App.Notifications.NotificationTypes`, -> `App.Notifications.NotificationRules`, `App.BackgroundWorkers.RecurringJobs`, `App.Definitions.Currency`, -> `App.Definitions.ContactTitle`) — bunlar platformun kendi verisidir, proje talebi için tercih edilmez. +> | Durum | Ekranlar (örnek) | Prompt ile kayıt eklerken | +> | --- | --- | --- | +> | Seed bayrakları açık — ekrandan yapılan kayıt dosyaya yansır | RecurringJobs, NotificationTemplates/Types/Rules, SettingDefinitions, AiBot, CustomEndpoints, DynamicServices, CustomComponents, McpServers, IntranetWidgets, `App.Menus.*`, `Abp.Identity.Permission*` | Satırı `data/{ListFormCode}.json`'a yaz | +> | `SeedFilePath` dolu, bayraklar kapalı — dosya migrate'te uygulanır, ekrandan yapılan değişiklik dosyaya **yansımaz** | Sequence, GlobalSearch, Currency, ContactTitle, Country/City/District, Uom, Department, JobPosition, ReportTemplates/Categories, WorkHour, Intranet, Blog, Forum, Orders tanımları | Satırı dosyaya yaz; ekrandan eklenen kayıt teslim sayılmaz | +> | `SeedFilePath` boş — yalnızca veritabanı | DataSource, Languages, Branches, Roles/Users/Tenants, AuditLogs, IpRestrictions, Notification (geçmiş), BackgroundWorkers.Jobs | `sql/execute/{Ad}.sql` altında **varsa güncelleyen/yoksa ekleyen** bir prosedür (§8.4) | > -> "Ekranı ürettim ama numaratörü/zamanlanmış işi de kurdum" diyorsan, o kayıtların seed karşılığını -> da yazmadıysan teslim eksiktir. +> Tam liste `ListFormSeeder_Saas.cs` / `ListFormSeeder_Administration.cs` içindeki `SeedFilePath` +> ve `SeedSync*` atamalarıdır. "Ekranı ürettim ama numaratörü/zamanlanmış işi de kurdum" diyorsan, +> o kayıtların seed karşılığını da yazmadıysan teslim eksiktir. --- -## 9.1. Artefakt G — Liste verisi seed dosyası (`data/{ListFormCode}.json`) +### 9.15 Artefakt G — Liste verisi seed dosyası (`data/{ListFormCode}.json`) Ekrandan girilen kayıtların (widget tanımı, parametre tablosu, sabit liste) veritabanı sıfırlandığında geri gelmesi için kullanılır. Bağlantı ListForm kaydındaki `SeedFilePath` alanıdır; boşsa ne geri yükleme ne de senkronizasyon yapılır. -**Hedef dosya ya da klasördür.** `SeedFilePath` `.json` ile bitiyorsa bütün satırlar tek +**Hedef dosya ya da klasördür.** Klasör modunda da her dosya aynı zarfı (`ListFormCode`, +`KeyFieldName`, `GeneratedAt`, `Order`, `Rows`) taşır; `Rows` tek satırdır ve dosya adı +`{anahtar}.json`'dur. Ekranın `SeedFilePath`'i varsayılan `data/{ListFormCode}.json` iken +diskte `data/{ListFormCode}/` klasörü varsa migrate yolu klasöre çevirir. `SeedFilePath` `.json` ile bitiyorsa bütün satırlar tek dosyadadır (varsayılan). Uzantısız yazılırsa hedef klasördür (`data/{ListFormCode}`) ve her satır `{anahtar}.json` olarak kendi dosyasında durur; klasör yoksa ilk kayıtta oluşturulur. Klasör modu satırı büyük ya da sık yazılan ekranlar içindir — şu an tek örnek Custom Components @@ -3692,26 +3793,29 @@ kapalıyken dosya yalnızca seed sırasında okunur, hiç yazılmaz. | Alan | Sözleşme | | --- | --- | -| `ListFormCode` | Verinin ait olduğu ekran; dosya adıyla aynı olmalı. Boşsa dosya adı kullanılır | -| `KeyFieldName` | Satırların eşleştiği anahtar alan; boşsa `ListForm.KeyFieldName` | -| `Order` | Uygulama sırası; küçük olan önce uygulanır (yabancı anahtarlı tablolarda önce tanım dosyası). Yoksa `0`, eşitlikte dosya adına göre sıralanır | +| `ListFormCode` | Verinin ait olduğu ekran; dosya adıyla aynı olmalı. Boşsa dosya adı (klasör modunda klasör adı) kullanılır | +| `KeyFieldName` | Satırların eşleştiği anahtar alan; **dosyadaki değer ekranınkini ezer**, boşsa `ListForm.KeyFieldName`. İş anahtarıyla eşleşen dosyalar (`Abp.Identity.Permission*` → `Name`, `App.Menus.Menu` → `Code`) `Id` taşımaz | +| `Order` | Uygulama sırası; küçük olan önce uygulanır (route → modül → menü, yetki grubu → yetki, ülke → il → ilçe, bildirim tipi → kural). Yoksa `0`, eşitlikte yola göre sıralanır. Klasör modunda ilk dosyadan okunur; senkronizasyon yeni satır dosyasına kardeşinin değerini kopyalar | | `Rows` | Ekranın select alanlarından oluşan satırlar. Enum'lar **sayı** olarak yazılır (SQL kolonu ne ise o); dil anahtarları `::` öneki olmadan yazılır | Kurallar: - **Anahtar dosyada durur.** Insert varsayılanları anahtarı `@NEWID` ile üretir; `ListFormSeedDataApplier` dosyadaki anahtarı geri yazar, böylece aynı dosya birden çok kez uygulandığında kopya kayıt oluşmaz. - Elle dosya yazarken anahtarı deterministik üret (ör. koddan türeyen GUID). + Elle dosya yazarken anahtarı sabit yaz: `Guid` anahtarlı satırda iş anahtarından türetilmiş + (v5 biçimli) sabit bir GUID; her çalıştırmada yeni GUID üretme. - **Identity anahtarlı tablolarda satır anahtarsız yazılır.** `Id` alanı dosyada yoksa anahtar veritabanına bırakılır; satırın var olup olmadığı dosyadaki **tüm alan değerlerinin** - (tenant kapsamı dahil) eşleşmesine bakılarak belirlenir. + (tenant kapsamı dahil) eşleşmesine bakılarak belirlenir — bu satırlar hiçbir zaman güncellenmez. - **Dosyadaki değer varsayılanı ezer.** `InsertFieldsDefaultValueJson` yalnızca dosyada bulunmayan alanları doldurur (audit kolonları, kapsam, anahtar); ekrandan kayıtta ise varsayılan girilen değeri ezer. - **`@USER:kullanıcıAdı`** değeri seed sırasında o kullanıcının anahtarına çözülür; kullanıcı anahtarları her kurulumda yeniden üretildiği için dosyaya GUID yazılmaz. Kullanıcı bulunamazsa alan boş kalır. -- **Uygulama yalnızca ekler:** anahtarı veritabanında bulunmayan satırlar `INSERT` edilir; var olan +- **Migrate yalnızca ekler:** anahtarı veritabanında bulunmayan satırlar `INSERT` edilir; var olan kayıt güncellenmez (her migrate'te ekrandaki güncel değerler dosyadaki eskisiyle ezilmesin diye) - ve dosyada olmayan satır silinmez. + ve dosyada olmayan satır silinmez. Wizard import'unun kapanışı ise `overwriteExisting` ile + çalışır: var olan satır güncellenir, soft delete izi temizlenir (§7.7). Her hedef kendi unit of + work'ündedir; birinin hatası diğerlerini durdurmaz. - **Yazma yönü bayrağa bağlıdır:** `list-form-data/*` ve `list-form-dynamic-api/*` uçlarında insert → `SeedSyncInsert`, update → `SeedSyncUpdate`, delete → `SeedSyncDelete`; ImportManager toplu yüklemesi `SeedSyncInsert` bayrağına bakar ve dosyayı listenin tamamıyla yeniden yazar. @@ -3776,7 +3880,8 @@ geçerlidir: var olan bileşene eklenir, yeni bileşen ya da Developer Kit ekran 1. Önce gerçekten çözülemediğini göster: ListForm + SQL + Custom Endpoint neden yetmiyor, bir iki cümleyle yaz. -2. Dynamic Service oluştur; `PlatformAppService` türet, `[Authorize(...)]` ile koru. +2. Dynamic Service oluştur; sınıf `ApplicationService`'ten (ya da ondan türeyen + `PlatformAppService`'ten) türesin, adı `AppService` ile bitsin, `[Authorize(...)]` ile koru (§9.3.1). 3. **TestCompile** → hatasız → **Publish**. `CompilationStatus` `Success` olmadan yayınlama. 4. Swagger'da `ControllerName` altında göründüğünü doğrula. 5. Ekran tarafında bu servisi ya ListForm'un `*ServiceAddress` alanından ya da Custom @@ -3810,8 +3915,8 @@ isteniyorsa geçilir. - `IsTenant: true` (tabloda `TenantId` var), `IsDeletedField: true`, `IsCreatedField: true` - `MenuParentCode` = modül kökü (`App.Hr`); yoksa wizard yaratır, `Order = max + 1` - `PermissionGroupName` = `App.Hr`, EN + TR görünen adlarıyla - - Görünümler: `Grid` + `Card` açık; durum akışı görsel isteniyorsa `Todo: true` + - `TodoOptionDto` (§9.8) + - Görünümler: varsayılan dörtlü (`Grid` + `Card` + `Pivot` + `Chart`, `DefaultLayout: "grid"`); + durum akışı görsel isteniyorsa ek olarak `Todo: true` + `TodoOptionDto` (§9.8) 3. **Alanlar** — `Groups` içinde iki grup mantıklıdır: "Talep" (tutar, tarih, açıklama) ve "Onay" (durum, onaylayan, onay tarihi, onay notu). Onay grubundaki alanlar `EditorOptions: "{\"readOnly\":true}"` ile kilitlenir; kullanıcı doldurmaz, akış doldurur. @@ -3848,10 +3953,13 @@ tedarikçi lookup'ı + iki kademeli onay). - [ ] `DeleteCommand = DefaultDeleteCommand("{Tablo}")`. - [ ] Seed dosyası oluştu; `InsertedRecords` yalnızca gerçekten yaratılan kayıtları içeriyor. - [ ] Bağımlılıklar (custom component, crud endpoint, sql nesnesi) da seed'li. -- [ ] Yalnızca veritabanında yaşayan konfigürasyon (Sequence, zamanlanmış iş, bildirim kuralı, - rapor şablonu) üretildiyse `sql/execute/` altında geri getirici script'i de yazıldı (§9.14). +- [ ] Hazır ekranlara eklenen kayıtlar (Sequence, zamanlanmış iş, bildirim tipi/kuralı, Custom + Endpoint…) ekranın `data/` dosyasına yazıldı; `SeedFilePath`'i olmayan ekranın kaydı için + `sql/execute/` altında geri getirici prosedür var (§9.14). - [ ] Export zip'i başka bir ortamda açıldığında ekran ayağa kalkıyor. -- [ ] Geri alma yolu yazılı: wizard dosyasını sil → `InsertedRecords` temizlenir. +- [ ] Geri alma yolu yazılı: Wizard Manager'dan ya da MCP `delete_wizard` ile sil → dosya ve + `InsertedRecords` içindeki kullanılmayan kayıtlar gider (§4.5). Dosyayı diskten silmek + kayıtları temizlemez. Prompt ile üretim yaptıysan ek olarak: @@ -3860,12 +3968,16 @@ Prompt ile üretim yaptıysan ek olarak: - [ ] Tablo/kolon adları uydurulmadı; ya var olan nesneden okundu ya da `sql/object/` altında üretildi. - [ ] `KeyFieldDbSourceType`, tablodaki `Id` tipiyle uyumlu. +- [ ] Liste ekranında `Grid`/`Card`/`Pivot`/`Chart` açık, `DefaultLayout: "grid"`; kapalı olan + görünüm yalnızca istek onu açıkça dışarıda bıraktığı için kapalı (§9.8). - [ ] Açılan her görünümün zorunlu option alanı dolu (§9.8). - [ ] Custom component'te route yetkisiz üretildiği için koruma bileşenin içinde (`designerPermission` / `*Permission` / `checkPermission`). - [ ] Custom component visual modda ise `Props.visualDesigner` ile `Code` marker'ı aynı dokümanı taşıyor. - [ ] Menü ve route elle eklenmedi; wizard dosyasından geliyor. +- [ ] List ekranı deploy ile kuruldu (wizard ekranı / MCP `deploy_wizard`) ya da elle yazılan + dosya `ListForm` + `Fields` bölümlerini taşıyor (§4.1). - [ ] Kullanıcıya hangi dosyaların üretildiği ve DB Migrate gerektiği söylendi. - [ ] Keşif turu işletildi (§0.6): Tur 1 soruları soruldu, yol için gerekçe verildi ve tarif üretimden **önce** onaylatıldı — kullanıcı "sorma / sen seç" demedikçe. diff --git a/README.md b/README.md index 50b33308..f37162a2 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ React/C# kodu yazmadan, yalnızca konfigürasyon ile mümkündür. | **Custom Component** | Babel ile tarayıcıda derlenen, veritabanında saklanan React bileşenleri ve bunların route'ları. | | **İş akışı / onay** | ListForm üzerinde görsel workflow tasarımcısı, koşullu dallanma ve çok kişili onay adımları. | | **Çok kiracılılık** | Tenant başına ayrı bağlantı dizesi (ayrı veritabanı) desteği, şube (branch) ve organizasyon birimi kırılımı. | -| **Bildirim ve entegrasyon** | SMS, e-posta, WhatsApp, Rocket.Chat, masaüstü ve UI kanallarına kural tabanlı bildirim. | +| **Bildirim ve entegrasyon** | SMS, e-posta, WhatsApp, Telegram, Rocket.Chat, masaüstü ve UI kanallarına kural tabanlı bildirim. | | **Raporlama** | DevExpress Report Designer/Viewer + ListForm'dan otomatik üretilen dinamik raporlar. | | **Arka plan işleri** | Hangfire + ABP Background Worker; zamanlanmış SQL, mail kuyruğu, yedekleme, oturum temizliği. | | **Gerçek zamanlı** | SignalR tabanlı messenger ve video oda (WebRTC/TURN) altyapısı. | @@ -963,7 +963,7 @@ içindedir; oradaki kontroller yalnızca butonları gizler, asıl kontrol AppSer | Aktivite ve denetim kaydı | `/admin/activityLog`, `/admin/changeLog` | Kullanıcı hareketleri ve ABP audit log detayı. | | Bildirimler | `/admin/profile/notification-settings`, bildirim ekranları | Kanal tercihleri ve bildirim oluşturma. | | Forum | `/admin/forum` | Forum kullanımı; yönetim `App.ForumManagement` altındaki ListForm ekranlarıyla yapılır. | -| Intranet | Intranet dashboard ve widget'ları | Duyuru, anket, sosyal duvar, etkinlik. Hangi widget'ın hangi kolonda, hangi sırada ve hangi yetkiyle yayınlandığı `Adm_T_IntranetWidget` kaydından gelir; kayıtlar `configs/seeds/{kapsam}/data/App.DeveloperKit.IntranetWidgets.json` ile senkron tutulur. | +| Intranet | Intranet dashboard ve widget'ları | Duyuru, anket, sosyal duvar, etkinlik. Hangi widget'ın hangi kolonda, hangi sırada ve hangi yetkiyle yayınlandığı `Sas_T_IntranetWidget` kaydından gelir; kayıtlar `configs/seeds/{kapsam}/data/App.DeveloperKit.IntranetWidgets.json` ile senkron tutulur. | | Video oda | `/admin/videoroom/*` | Oda listesi, detay, planlama, katılımcı ve ekran paylaşımı. | | Raporlar | `/admin/reports/:report/view|design/...` | DevExpress rapor görüntüleyici ve tasarımcı. | | AI asistanı | `/admin/ai` | n8n webhook üzerinden sohbet arayüzü. | @@ -1184,7 +1184,14 @@ rota ve menüleri. ### 13.1. Kanallar -`Sms` · `Mail` · `Rocket` · `Desktop` · `UiActivity` · `UiToast` · `WhatsApp` (`Telegram` altyapıda tanımlı, UI'da kapalı) +`Sms` · `Mail` · `Rocket` · `Desktop` · `UiActivity` · `UiToast` · `WhatsApp` · `Telegram` + +Kanal kayıt defteri iki yerdedir: backend `NotificationChannels` (Domain.Shared), UI +`constants/notification-channel.enum.ts` (kanal değeri + `NotificationChannelColors` + +`NotificationChannelIcons`). Kanal listesi gösteren ekranlar (tercih matrisi, Activity Log filtresi) +listeyi bu kayıt defterinden üretir; etiket her yerde `App.NotificationChannel.{Kanal}` dil +anahtarından okunur, kanal lookup'ı (`ListFormSeeder_DefaultJsons`) da aynı anahtarları kullanır. +Yeni kanal = iki kayıt defteri + renk + ikon + dil anahtarı + `NotificationWorker` gönderimi. ### 13.2. Yönlendirme modeli @@ -1248,7 +1255,12 @@ kayıt üretilir. Tercih ekranı (`/admin/profile/notification-settings`) satırlarını bildirim **tipinin id'si** üzerinden eşler; tip adı yalnızca gösterimdir. `NotificationType.Description` alanı aynı ekranda -ipucu olarak görünür ve Bildirim Türleri ekranından yazılır. +ipucu olarak görünür ve Bildirim Türleri ekranından yazılır. Ekran geniş ekranda tür × kanal +matrisi, dar ekranda kart listesidir; hücre `Switcher`'dır, `IsFixed` kural kilit ikonuyla +gösterilir ve değiştirilemez, kullanıcının değiştirdiği tür işaretlenir. Kayıt sırasında yalnızca +ilgili hücre/satır kilitlenir. Satırı varsayılana döndürmek +`DELETE /api/app/notification-rule/my-notification-rules/{notificationTypeId}` ile kullanıcının o +türdeki tercihlerini siler. ### 13.5.1. Bildirim ekranı ve elle gönderim (Administration › Bildirimler) @@ -1311,6 +1323,7 @@ gönderimde ekrandan ya da şablondan gelir — yani konu artık gövdeye gömü | SMS | Posta Güvercini HTTP API | | WhatsApp | WhatsApp Cloud API (şablon tabanlı) | | Rocket.Chat | Rocket.Chat HTTP API | +| Telegram | Telegram Bot API (`SozsoftTelegramSender`; alıcı kullanıcının `TelegramChatId` özelliği) | Tüm sağlayıcı kimlik bilgileri ayar ekranından yönetilir (`App.Settings.*` anahtarları); koda gömülmez. @@ -1871,7 +1884,6 @@ configs/seeds/ `mrp`, `accounting` ve `hr` yol sabitleri **yol sözleşmesidir**; bu alanların ekranları fiziksel React sayfaları olarak değil, ListForm konfigürasyonu ile üretilir. `hr` altında yalnızca organizasyon şeması (`OrgChart.tsx`) fiziksel bir view'dır. -- `Telegram` bildirim kanalı altyapıda tanımlıdır ancak UI'da kapalıdır. - Depodaki `appsettings*.json` ve deployment dokümanları geliştirme değerleri/örnek kimlik bilgileri içerir; üretim öncesi tümü değiştirilmelidir. - SQL View Designer tek yönlüdür (model → T-SQL). Elle yazılmış ya da tasarımcının kanonik diff --git a/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.HttpApi/Tools/McpDevelopTools.cs b/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.HttpApi/Tools/McpDevelopTools.cs index b8f713c5..0c78555b 100644 --- a/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.HttpApi/Tools/McpDevelopTools.cs +++ b/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.HttpApi/Tools/McpDevelopTools.cs @@ -59,7 +59,7 @@ public sealed class McpDevelopTools(IMcpDevelopAppService developAppService) => await developAppService.GetWizardAsync(fileName); [McpServerTool(Name = McpToolNames.DeployWizard, Destructive = true, Idempotent = false)] - [Description("Wizard'i deploy eder: ListForm ekrani (ComponentKind 0) ya da bir custom component'i menuye baglayan kayit (ComponentKind 1), yetkileri, dil anahtarlari, menusu ve seed dosyasi birlikte uretilir. Hedef tablo onceden var olmalidir; yoksa once create_table ile olustur. replaceFileName verilirse o wizard ve urettigi kayitlar silinip yeniden uretilir; ekran wizard cevaplarindan yeniden turetildigi icin tasarimcida yapilmis ve wizard'da karsiligi olmayan ayarlar kaybolur. Ne yazacagini get_dev_guide 4. bolumunden ogren.")] + [Description("Wizard'i deploy eder: ListForm ekrani (ComponentKind 0) ya da bir custom component'i menuye baglayan kayit (ComponentKind 1), yetkileri, dil anahtarlari, menusu ve seed dosyasi birlikte uretilir. Liste ekraninda Grid, Card, Pivot ve Chart gorunumleri varsayilan aciktir (DefaultLayout grid); kullanici bir gorunumu istemedigini soylemedikce bu bayraklari false yazma. Hedef tablo onceden var olmalidir; yoksa once create_table ile olustur. replaceFileName verilirse o wizard ve urettigi kayitlar silinip yeniden uretilir; ekran wizard cevaplarindan yeniden turetildigi icin tasarimcida yapilmis ve wizard'da karsiligi olmayan ayarlar kaybolur. Ne yazacagini get_dev_guide 4. bolumunden ogren.")] public async Task DeployWizardAsync( [Description("Wizard JSON'u: seed dosyasi bicimi ({\"Wizard\": {...}}, get_wizard cikisi gibi) ya da duz wizard cevaplari. Enum alanlari sayi olarak yazilir.")] string wizard, diff --git a/api/src/Sozsoft.Platform.Application.Contracts/ListForms/GridOptionsDto/LayoutDto.cs b/api/src/Sozsoft.Platform.Application.Contracts/ListForms/GridOptionsDto/LayoutDto.cs index 40f91f35..a38f56f3 100644 --- a/api/src/Sozsoft.Platform.Application.Contracts/ListForms/GridOptionsDto/LayoutDto.cs +++ b/api/src/Sozsoft.Platform.Application.Contracts/ListForms/GridOptionsDto/LayoutDto.cs @@ -6,10 +6,10 @@ public class LayoutDto public bool Card { get; set; } = true; public bool Pivot { get; set; } = true; public bool Chart { get; set; } = true; - public bool Tree { get; set; } = true; - public bool Gantt { get; set; } = true; - public bool Scheduler { get; set; } = true; - public bool Todo { get; set; } = false; + public bool Tree { get; set; } + public bool Gantt { get; set; } + public bool Scheduler { get; set; } + public bool Todo { get; set; } public string DefaultLayout { get; set; } = "grid"; } diff --git a/api/src/Sozsoft.Platform.Application.Contracts/ListForms/Wizard/ListFormWizardDto.cs b/api/src/Sozsoft.Platform.Application.Contracts/ListForms/Wizard/ListFormWizardDto.cs index 59499861..fcc0c95a 100644 --- a/api/src/Sozsoft.Platform.Application.Contracts/ListForms/Wizard/ListFormWizardDto.cs +++ b/api/src/Sozsoft.Platform.Application.Contracts/ListForms/Wizard/ListFormWizardDto.cs @@ -77,12 +77,14 @@ public class ListFormWizardDto public bool AllowDetail { get; set; } public bool ConfirmDelete { get; set; } - public string DefaultLayout { get; set; } - public bool Grid { get; set; } - public bool Card { get; set; } - public bool Pivot { get; set; } + // Liste ekranı varsayılan olarak dört analiz görünümüyle gelir (UI wizard ve LayoutDto ile aynı); + // MCP/prompt ile gelen cevapta bayrak yazılmazsa görünüm sessizce kapanmasın diye. + public string DefaultLayout { get; set; } = "grid"; + public bool Grid { get; set; } = true; + public bool Card { get; set; } = true; + public bool Pivot { get; set; } = true; + public bool Chart { get; set; } = true; public bool Tree { get; set; } - public bool Chart { get; set; } public bool Gantt { get; set; } public bool Scheduler { get; set; } public bool Todo { get; set; } diff --git a/api/src/Sozsoft.Platform.Domain.Shared/WizardConsts.cs b/api/src/Sozsoft.Platform.Domain.Shared/WizardConsts.cs index d304258b..c424b48c 100644 --- a/api/src/Sozsoft.Platform.Domain.Shared/WizardConsts.cs +++ b/api/src/Sozsoft.Platform.Domain.Shared/WizardConsts.cs @@ -41,7 +41,7 @@ public static class WizardConsts Margin = 10 }); - public static string DefaultLayoutJson(string DefaultLayout = "grid", bool Grid = true, bool Card = true, bool Pivot = true, bool Chart = true, bool Tree = true, bool Gantt = true, bool Scheduler = true, bool Todo = true) => JsonSerializer.Serialize(new + public static string DefaultLayoutJson(string DefaultLayout = "grid", bool Grid = true, bool Card = true, bool Pivot = true, bool Chart = true, bool Tree = false, bool Gantt = false, bool Scheduler = false, bool Todo = false) => JsonSerializer.Serialize(new { Grid = Grid, Card = Card, diff --git a/claude.md b/claude.md index 4559483c..00d27389 100644 --- a/claude.md +++ b/claude.md @@ -156,6 +156,10 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` → migration, platform tablosu şeması, yeni paket, `Routes`, dış entegrasyon — **önce sorulur, yalnızca onayla yapılır**; onay talebe özeldir. Çerçeve içinde yapılabilen kısım sorudan önce bitirilir. "Yapılamaz" cevabı yoktur. Listeler: `lowcode.instructions.md` §0.8. +12. **Liste ekranı görünümleri** → prompt/görsel/tarifle üretilen her liste ekranı `Grid` + `Card` + + `Pivot` + `Chart` açık, `DefaultLayout: "grid"` ile gelir; "liste/grid" demek diğerlerini + kapatmak değildir. Bir görünüm yalnızca istek onu açıkça dışarıda bıraktığında `false` yazılır. + Tree/Gantt/Scheduler/Todo yalnızca istenince açılır. Ayrıntı: `lowcode.instructions.md` §9.8. ### Cross-cutting: touching one thing means touching these too diff --git a/configs/ai/Chat.json b/configs/ai/chat_SQL_gemma3_4b_Select_Summary_Pivot_Grouping.json similarity index 100% rename from configs/ai/Chat.json rename to configs/ai/chat_SQL_gemma3_4b_Select_Summary_Pivot_Grouping.json diff --git a/configs/ai/Kurulum.txt b/configs/ai/install_ollama.txt similarity index 100% rename from configs/ai/Kurulum.txt rename to configs/ai/install_ollama.txt