diff --git a/.github/instructions/ai.instructions.md b/.github/instructions/ai.instructions.md index 83465758..832b02ae 100644 --- a/.github/instructions/ai.instructions.md +++ b/.github/instructions/ai.instructions.md @@ -19,6 +19,7 @@ Companion documents (this file wins on conflict): | Document | Scope | | --- | --- | | `lowcode.instructions.md` | **Authoring reference**: exact schemas, enum values and worked examples for producing wizard screens, editor scripts, custom components, SQL objects and endpoints. Read it before writing any artifact. | +| `lowcode-reference.instructions.md` | **Capability inventory**: field-by-field contract of every ListForm/ListFormField JSON column, chart and pivot options, SQL command hooks, Visual Designer toolbox, the scheduled-mail module and (§12) every ready-made screen under the Saas and Administration menus. Sweep its §0 checklist before producing a screen or component; a request whose answer already exists as a platform screen (§12.5) is configured there, not rebuilt. | | `dotnet.instructions.md` | Binding code standard for everything under `api/` | | `list.instructions.md` | Step-by-step procedure for adding a module / list through seeders | | `../../README.md` | Technical map: what exists, where it lives, how it is wired | @@ -51,11 +52,25 @@ System nature: 3. Every proposal must include tenant and permission design. 4. Never bypass platform authorization patterns. 5. Never hardcode secrets, tenant ids, or connection strings. +6. **Stay inside the framework; ask before stepping out.** The platform may extend itself through + its own mechanisms (seed artifacts; adding a missing event, prop, or small backward-compatible + capability to an existing UI component — when Developer Kit tooling builds ~70% of the + requested component, the rest is completed with such small additions, never with a new + component; backward-compatible additions to script recipes / editor options / tokens). + Anything beyond that — a new React component or page, a new toolbox family, core engine changes (ListForm + runtime, seeders, code generator, auth, DataSourceManager, dispatcher/compiler), new ABP + entity/module/migration, platform table schema changes, new NuGet/npm dependencies, `Routes` + in `MenusData.json`, new external integrations — is **out of frame**: finish everything that + fits inside the frame first, then ask the user with a single question (why configuration is + insufficient, the smallest change that works, files touched, rollback) and proceed **only with + explicit approval**. Approval is per request and does not carry over. Full lists: + `lowcode.instructions.md` §0.8. Exception policy: - Code-level React or backend development can be considered only if user explicitly requests code implementation and configuration path is insufficient. - If exception is required, AI must explain why configuration-based options are not enough. +- Never say "cannot be done": the answer is either an in-frame build or an approval question. --- @@ -293,6 +308,13 @@ canonical shape drops the dialog to raw SQL mode, so hand-editing a designed vie designer. Aggregations available in the Group By column: `GroupBy`, `Where`, `SUM`, `COUNT`, `COUNT_DISTINCT`, `AVG`, `MIN`, `MAX`. +Persistence: only the **table designer's** deploy writes a seed file automatically +(`{sql|postgres}/object/{TableName}.sql`, full CREATE script, via `save-table-script`). View +Designer deploys and any DDL run from the SQL editor (views, procedures, functions) reach the +database only — their seed files under `{sql|postgres}/object/` must be written explicitly. +Full mechanics and the SP/function production patterns: `lowcode.instructions.md` §8.1–§8.2.1 +and §9.5. + --- ## 11. Integrations @@ -423,7 +445,8 @@ When configuration cannot satisfy requirement: 1. Try SQL-based design 2. Try Custom Endpoint 3. Try Dynamic Service -4. Propose minimal code change with explicit justification +4. Propose minimal code change with explicit justification — this step is out of frame: + **ask and wait for approval before touching code** (rule 3.6). --- @@ -759,26 +782,38 @@ When user asks for a screen/module, AI must answer in this order: AI should produce practical, copy-adaptable artifact definitions and avoid abstract-only explanations. -### 31.9 Screen brief and image-based requests +### 31.9 Screen brief, discovery questions and image-based requests A request arrives either as prose or as a **screenshot / mockup / photo** of the desired screen. -Both enter the same pipeline: +Both enter the same pipeline — and for a **new screen/component the pipeline starts with +questions, not files**: -1. Turn it into a single screen brief — purpose, data source, columns and editors, layouts, - actions, sub-screens, widgets, approval flow, menu, permissions, tenancy, language texts. - For an image, read it first: page regions → main region type → toolbar → columns → filters → - form controls → row actions. -2. Pick the tool: grid-shaped work → SQL Query Manager + Wizard; free layout / dashboard / - single-record custom form → Custom Component. **When in doubt, pick the Wizard.** -3. Produce the seed files in dependency order. +1. **Run the discovery round** (`lowcode.instructions.md` §0.6). Ask in batches (max 4 questions + per round, each with 2–4 options and a recommended default), max three rounds. Round 1 always + covers: screen kind (List/Wizard vs Custom Component vs single-record form), data source, + owning module, and tenancy scope. Round 2 uses the path-specific question set (§0.6.3 for + List, §0.6.4 for Custom). Never ask what the repo already answers — search first, confirm + findings instead of re-asking. If the user says "you decide / don't ask", skip the round and + build on defaults. +2. Turn the answers into a single screen brief — purpose, data source, columns and editors, + layouts, actions, sub-screens, widgets, approval flow, menu, permissions, tenancy, language + texts. For an image, read it first (page regions → main region type → toolbar → columns → + filters → form controls → row actions); the image answers most of round 2, so ask only what + an image cannot show (§0.7.4). +3. **Get the brief confirmed before writing any seed file.** Mark defaulted lines `(varsayılan)` + so the user sees what they are approving. +4. Pick the tool: grid-shaped work → SQL Query Manager + Wizard; free layout / dashboard / + single-record custom form → Custom Component. **When in doubt, recommend the Wizard.** +5. Produce the seed files in dependency order, then list them and restate the defaulted choices. An image is a reference, not a pixel contract: build the closest thing the platform's own -components give, and state at the end what had no equivalent. Never block on missing detail — -state the assumption and continue; ask only where a wrong guess loses data or picks the wrong -approver. +components give, and state at the end what had no equivalent. Three things are never assumed, +always asked: which table is written to, who approves (when there is an approval flow), and the +formula of any calculation/business rule. This question-first rule applies only to producing a +**new** screen/component; fixes, field additions, debugging and explanations proceed directly. -The concrete brief template, the image-element → artifact mapping and the visual-control → -`EditorType` mapping are in `lowcode.instructions.md` §0.6–§0.7. +The concrete question sets, brief template, the image-element → artifact mapping and the +visual-control → `EditorType` mapping are in `lowcode.instructions.md` §0.6–§0.7. --- diff --git a/.github/instructions/lowcode-reference.instructions.md b/.github/instructions/lowcode-reference.instructions.md new file mode 100644 index 00000000..423d0a39 --- /dev/null +++ b/.github/instructions/lowcode-reference.instructions.md @@ -0,0 +1,1373 @@ +# Low-Code Genişletilmiş Referans (JSON Kolon ve Modül Sözleşmeleri) + +`lowcode.instructions.md` **nasıl üretileceğini** anlatır; bu dosya **neyin ayarlanabildiğini** +tek tek listeler. Orada "bunun bir JSON kolonu var" denen her şeyin alan alan karşılığı burada +durur: ListForm/ListFormField'ın tüm JSON kolonları, grafik sözleşmesi, Visual Designer envanteri +ve zamanlanmış e-posta modülü. Son bölüm (§12) Saas ve Administration menülerindeki **hazır +gelen bütün ekranları** ve bunların yeni bir komponentte nasıl kullanılacağını listeler. + +**Kullanım sırası:** `ai.instructions.md` → `lowcode.instructions.md` → **bu dosya** → `README.md`. +Bir istek geldiğinde önce ana dosyadaki karar sırasını işlet; "hangi anahtarı yazacağım?" +sorusunda buraya bak. + +**Kaynak:** Buradaki her tablo `api/src/Sozsoft.Platform.Application.Contracts/ListForms/**` ve +`api/src/Sozsoft.Platform.Domain/Entities/Tenant/ListForm/ListForm.cs` dosyalarından çıkarılmıştır. +Çelişki hâlinde **kod esastır**; bu dosya güncellenir. + +**Ortak kurallar** + +- Bütün `*Json` kolonları veritabanında **tek satır JSON string** olarak durur; wizard seed + dosyasına yazarken kaçışlanır. +- 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: + - 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). + - Yanlış **tipte** yazılan değer (`"3"` yerine `3`, dizi yerine metin) `JsonSerializer` + tarafında `JsonException` fırlatır ve ekranın metadata isteği tamamen düşer — sessiz + değil, gürültülü bir hatadır. Tipleri tablodaki gibi yaz. + - Bu kural yalnızca alan seviyesindeki `ListFormField.EditorOptions` için geçmez: orası + ham metin olarak taşınır, tanımadığı anahtarı doğrudan editöre geçirir (§9.8). + +--- + +## 0. Üretim yetenek envanteri — bir komponent üretirken tara + +Bir ekran/komponent üretirken **her katmanı sırayla geç**. Cevabı "gerekmiyor" olan katmanı atla, +ama hiçbirini görmeden bırakma; kullanıcı "şunu da yapabilir miydik?" dediğinde cevap +"kod yazmamız gerekir" olmasın. + +| # | Katman | Sorulacak soru | Nerede | +| --- | --- | --- | --- | +| 0 | **Hazır yetenek** | Bu talebin karşılığı Saas/Administration menüsünde zaten var mı? | §12.5 | +| 1 | **Veri** | Tablo/view var mı, join gerekiyor mu, anahtar ve tipi ne? | `lowcode` §8 · §9.6 | +| 2 | **Ekran türü** | Liste mi, tek kayıt formu mu, serbest yerleşim mi? Kaç görünüm açılacak? | `lowcode` §0.6.1 · §9.8 · §5.4 | +| 3 | **Sütunlar** | Editör tipi, biçim, hizalama, genişlik, band, gizli/aktif | `lowcode` §4.3 · §9.7 | +| 4 | **Lookup** | Sabit liste mi, sorgu mu, servis mi? Cascade var mı? | `lowcode` §4.3.4 | +| 5 | **Varsayılanlar** | Kullanıcı/tarih/tenant/sequence/sorgu ile otomatik dolacak alanlar | `lowcode` §4.3.5 · §9.9 | +| 6 | **Alan davranışı** | Hesap, kopyalama, temizleme, koşullu kilit, uyarı, servis çağrısı | `lowcode` §5 | +| 7 | **Editör görünümü** | Maske, ondalık, tarih biçimi, arama, çoklu seçim, görsel yükleme | `lowcode` §6 | +| 8 | **Doğrulama** | Zorunluluk, aralık, desen, karşılaştırma + sunucu tarafı kontrol | `lowcode` §4.3.6 · §4 | +| 9 | **Filtre/arama** | Filtre satırı, başlık filtresi, arama paneli, ek filtre çubuğu, grup paneli | §2.6 · `lowcode` §4.3.8 | +| 10 | **Özet** | Toplam/adet satırı, grup özetleri, pivot | §9.5 · §6 | +| 11 | **Biçimlendirme** | Koşullu satır/hücre rengi, CSS sınıfı | `lowcode` §4.3.7 | +| 12 | **Düzenleme deneyimi** | Popup/satır/hücre/batch, form grupları, kopyalama, toplu silme | §3 | +| 13 | **Aksiyonlar** | Toolbar ve satır butonları: adres, diyalog, script, yetki, koşullu görünürlük | `lowcode` §4.9 | +| 14 | **Yazma mantığı** | Özel SQL, before/after kancaları, farklı servis adresi | §4 | +| 15 | **Ana-detay** | Alt form sekmeleri ve ilişki alanları | §5.2 | +| 16 | **Gösterge** | Üstte KPI kartları, grafik | §5.3 · §8 | +| 17 | **Süreç** | Onay akışı, bilgilendirme, durum alanları | `lowcode` §4.7 | +| 18 | **Otomasyon** | Zamanlanmış iş, zamanlanmış e-posta, bildirim kuralı | `lowcode` §9.10–§9.11 · §11 | +| 19 | **Çıktı** | Hazır grid raporu ya da rapor şablonu | `lowcode` §9.12 | +| 20 | **Yetki** | Ekran, alan, komut ve buton yetkileri; menü görünürlüğü | §5.1 · `lowcode` §4.2 | +| 21 | **Tenant/şube** | `IsTenant`, `IsBranch`, `IsOrganizationUnit`, sorgularda kırılım | `lowcode` §4.2 | +| 22 | **Dil** | Menü, başlık, alan, buton, mesaj metinleri EN + TR | `lowcode` §4.2 | +| 23 | **Kullanıcı tercihi** | Grid durumunun saklanması, kullanıcı/rol/dil varyantı | §2.5 · §5.7 | +| 24 | **Seed** | Üretilen her artefaktın dosyası + yalnızca DB'de yaşayan kayıtların geri getirici script'i | `lowcode` §0.2 · §9.14 | + +Serbest yerleşimli bir komponent (Visual Designer) üretiyorsan 0–2 ve 13–24 aynen geçerlidir; +3–12 yerine tasarımcı envanteri (§10.1), `Form` sözleşmesi (§10.2), veri kaynağı/filtre +sözleşmesi (§10.4), bileşen prop sözleşmeleri (§10.5–§10.9), olay yükleri (§10.10) ve event +script'leri (`lowcode` §7.8) kullanılır. 20. katman (yetki) orada +iki yerdedir: düğüm başına `designerPermission` ve `Form`'un dört komut yetkisi. + +Katmanların çoğunda cevap yeni bir artefakt değil, platformun **hazır bir ekranıdır**: numaratör, +bildirim, zamanlanmış iş, rapor, ayar, dil, yetki, şube, kısıt, dosya. Hepsinin envanteri §12'de — +"kod yazmamız gerekir" demeden önce §12.5'e bak. + +--- + +## 1. ListForm JSON kolon haritası + +Bir List ekranının bütün davranışı bu kolonlardadır. "Şunu yapabilir miyiz?" sorusunun cevabı +önce bu tabloda aranır. + +Aşağıdaki tablo `*Json` bloklarını listeler. Bunların **öncesinde** ekranın düz kolonları gelir +ve çoğu isteğin cevabı aslında oradadır: + +| Kolon | Varsayılan | Ne yapar | +| --- | --- | --- | +| `ListFormCode` | — | Ekranın kimliği; route, metadata ve varyantlar bu koda bağlanır | +| `DataSourceCode` | `"Default"` | Hangi bağlantıdan okunacağı (`Sas_H_DataSource`) | +| `SelectCommandType` | `1` Table | `1` Table · `2` View · `3` TableValuedFunction · `4` Query · `5` StoredProcedure | +| `SelectCommand` | — | Tablo/view adı, fonksiyon çağrısı ya da sorgu metni | +| `TableName` | `SelectCommand` | Üretilen `SELECT`'te kolonların ve join'lerin önüne yazılan **alias**. Boşsa `SelectCommand` kullanılır; `SelectCommand` bir sorgu metni ya da fonksiyon çağrısıysa buraya kısa bir ad yaz | +| `KeyFieldName` · `KeyFieldDbSourceType` | — · `16` String | Anahtar kolon ve `System.Data.DbType` karşılığı | +| `SelectFieldsDefaultValueJson` | — | **Okuma** sorgusuna parametre olarak katılan değerler (`FieldsDefaultValueDto[]`) — `@USERID`, `@TENANTID` gibi | +| `DefaultFilter` | — | Her sorgunun sonuna `AND` ile eklenen sabit `WHERE` koşulu. `DefaultValueHelper` üzerinden geçer, yani `@USERID`/`@TENANTID`/`@NOW` gibi token'lar çözülür. Soft delete'li tabloda (`IsDeletedField: true`) wizard buraya `"IsDeleted" = 'false'` yazar | +| `SortMode` | — | `UiGridSortModeEnum`: `1` Single · `2` Multiple · `3` None | +| `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 | + +| Kolon | Ne yapılandırır | Bölüm | +| --- | --- | --- | +| `ColumnOptionJson` | Grid genel görünümü (kenarlık, satır rengi, kolon seçici, sabitleme) | §2.1 | +| `CommandColumnJson` | Toolbar ve satır butonları | `lowcode` §4.9 | +| `RowJson` | Satır yüksekliği ve metin kaydırma | §2.2 | +| `PagerOptionJson` | Sayfalama şeridi | §2.3 | +| `SelectionJson` | Seçim modu ve checkbox davranışı | §2.4 | +| `StateStoringJson` | Kullanıcının grid tercihlerinin saklanması | §2.5 | +| `FilterRowJson` · `HeaderFilterJson` · `FilterPanelJson` · `SearchPanelJson` · `GroupPanelJson` | Filtre/arama/gruplama panelleri | §2.6 | +| `ExtraFilterJson` | Ekran üstü ek filtre çubuğu | `lowcode` §4.3.8 | +| `EditingOptionJson` | Düzenleme modu, izinler, popup ayarları | §3.1 | +| `EditingFormJson` | Düzenleme formunun grup/sekme yerleşimi | §3.2 | +| `InsertCommand` · `UpdateCommand` · `DeleteCommand` (+ `*Before`/`*After`) | Özel SQL komutları ve kancalar | §4 | +| `*FieldsDefaultValueJson` | Otomatik doldurulan alanlar | `lowcode` §4.3.5 | +| `*ServiceAddress` | Yazma işlemini karşılayan uç | §4.3 | +| `PermissionJson` | Ekran yetkileri | §5.1 | +| `SubFormsJson` | Alt form sekmeleri | §5.2 | +| `WidgetsJson` | KPI kartları | §5.3 | +| `WorkflowJson` | Onay akışı | `lowcode` §4.7 | +| `LayoutJson` | Açık görünümler ve varsayılan | §5.4 | +| `PivotOptionJson` | Pivot görünümü | §6 | +| `TreeOptionJson` · `GanttOptionJson` · `SchedulerOptionJson` · `TodoOptionJson` | Ağaç/Gantt/Takvim/Kanban eşlemeleri | §7 | +| `SeriesJson` ve diğer 21 grafik kolonu | Chart görünümü | §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 | — | +| `ListFormType` · `IsSubForm` · `SubFormsListFormType` · `ShowNote` | Ekranın türü ve alt form davranışı | §5.6 | +| `CultureName` · `UserId` · `RoleId` | Aynı ekranın dil/kullanıcı/rol varyantı | §5.7 | + +--- + +## 2. Grid görünümü + +### 2.1 `ColumnOptionJson` (`GridColumnOptionDto`) + +| Anahtar | Varsayılan | Anlam | +| --- | --- | --- | +| `ShowBorders` | `true` | Dış kenarlık | +| `ShowRowLines` | `true` | Satır çizgileri | +| `ShowColumnLines` | `false` | Kolon çizgileri | +| `RowAlternationEnabled` | `true` | Tek/çift satır renklendirme | +| `AllowColumnReordering` | `true` | Kullanıcı kolon sırasını değiştirebilir | +| `AllowColumnResizing` | `true` | Kolon genişliği değiştirilebilir | +| `ColumnResizingMode` | `"widget"` | `nextColumn` \| `widget` | +| `ColumnAutoWidth` | `false` | İçeriğe göre genişlik | +| `ColumnFixingEnabled` | `false` | Kolon sabitleme menüsü | +| `ColumnChooserEnabled` | `false` | Kolon seçici düğmesi | +| `ColumnChooserMode` | `"dragAndDrop"` | `dragAndDrop` \| `select` | +| `ColumnHidingEnabled` | `true` | Dar ekranda kolon gizleme | +| `HoverStateEnabled` | `false` | Satır üzerine gelince vurgulama | +| `FocusedRowEnabled` | `true` | Odaklı satır | +| `ShowColumnHeaders` | `true` | Başlık satırı | +| `RtlEnabled` | `false` | Sağdan sola | + +### 2.2 `RowJson` (`GridRowDto`) + +| Anahtar | Varsayılan | Anlam | +| --- | --- | --- | +| `RowHeight` | `"auto"` | Satır yüksekliği (CSS) | +| `WhiteSpace` | `"normal"` | Uzun metnin kaydırılması | +| `OverflowWrap` | `"break-word"` | Kelime kırma | + +### 2.3 `PagerOptionJson` (`GridPagerOptionDto`) + +Adı yalnızca sayfalamayı çağrıştırsa da bu blok **kaydırma ve yükleniyor panelini** de taşır. + +| Anahtar | Varsayılan | Anlam | +| --- | --- | --- | +| `Visible` | `true` | Sayfalama şeridi | +| `ShowPageSizeSelector` | `false` | Sayfa boyu seçici | +| `ShowInfo` | `false` | "Page 1 of 4 (37 items)" bilgisi | +| `ShowNavigationButtons` | `true` | İleri/geri düğmeleri | +| `AllowedPageSizes` | `"10,20,50,100"` | **Virgüllü metin**, dizi değil | +| `InfoText` | `"Page {0} of {1} ({2} items)"` | `{0}` sayfa · `{1}` toplam sayfa · `{2}` kayıt | +| `DisplayMode` | `"adaptive"` | `adaptive` \| `compact` \| `full` | +| `ScrollingMode` | `"standard"` | `standard` \| `virtual` \| `infinite` — uzun listelerde `virtual` | +| `LoadPanelEnabled` | `"auto"` | **Metin** alanıdır: `"auto"` \| `"true"` \| `"false"` | +| `LoadPanelText` | `"Loading..."` | Yükleniyor metni | + +Sayfa başına kayıt sayısı ekranın `PageSize` alanındadır; `0` = tümü. `ScrollingMode` ile +`PageSize` birlikte düşünülür: `virtual`/`infinite` seçilip `PageSize: 0` yazılırsa sayfalama +devre dışı kalır ve tüm küme tek istekte çekilir. + +### 2.4 `SelectionJson` (`SelectionDto`) + +| Anahtar | Varsayılan | Değerler | +| --- | --- | --- | +| `Mode` | `"none"` | `single` \| `multiple` \| `none` | +| `AllowSelectAll` | `false` | | +| `SelectAllMode` | `"allPages"` | `allPages` \| `page` | +| `ShowCheckBoxesMode` | `"always"` | `always` \| `none` \| `onClick` \| `onLongTap` | + +Toolbar butonları seçili satırlarla çalışır (`lowcode` §4.9); onay akışı varsa wizard `single` yazar. + +### 2.5 `StateStoringJson` (`StateStoringDto`) + +| Anahtar | Varsayılan | Anlam | +| --- | --- | --- | +| `Enabled` | `true` | Kullanıcının kolon/filtre/sıralama tercihleri saklansın mı | +| `Type` | `"custom"` | `custom` (sunucuda kullanıcı bazlı) \| `localStorage` \| `sessionStorage` | +| `StorageKey` | — | Anahtar; boşsa ekran kodundan türer | +| `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. + +### 2.6 Filtre, arama ve gruplama panelleri + +**`FilterRowJson`** (`GridFilterRowDto`): `Visible` (`false`), `ApplyFilter` (`auto` \| `onClick`), +`AllowUserUiFilterSave` (varsayılan `true` — kullanıcı kendi filtresini kaydedebilir; +kayıt `ListFormCustomizationTypeEnum.UserUiFilter = 1` olarak tutulur). + +**`HeaderFilterJson`** (`GridHeaderFilterDto`): `Visible` (`false`), `AllowSearch` (`false`), +`Height` (`325`), `Width` (`252`), `SearchTimeout` (`500`). + +**`FilterPanelJson`** (`FilterPanelDto`): `Visible` (`false`), `FilterEnabled` (`false`), +`Texts.ClearFilter` (`"Clear"`) / `Texts.CreateFilter` (`"Create Filter"`) / +`Texts.FilterEnabledHint` (`"Enable the filter"`), `CustomizeText` (serbest JSON; filtre +metnini biçimlendirmek için — sözlükte tipi yoktur, olduğu gibi bileşene geçer). + +**`SearchPanelJson`** (`SearchPanelDto`): `Visible` (`false`), `Width` (`160`). + +**`GroupPanelJson`** (`GroupPanelDto`): `Visible` (`false`), `AutoExpandAll` (`false`). +`AutoExpandAll: true` **fazladan bir sorgu daha** çalıştırır. + +--- + +## 3. Düzenleme + +### 3.1 `EditingOptionJson` (`GridEditingDto`) + +| Anahtar | Varsayılan | Anlam | +| --- | --- | --- | +| `Mode` | `"popup"` | `batch` \| `cell` \| `row` \| `form` \| `popup` | +| `RefreshMode` | `"full"` | `full` \| `reshape` \| `repaint` | +| `AllowAdding` / `AllowUpdating` / `AllowDeleting` | `false` | Aksiyon izinleri (yetki ayrıca `PermissionJson`) | +| `AllowAllDeleting` | `false` | Toplu silme düğmesi | +| `AllowDuplicate` | `false` | Kaydı kopyalayarak yeni kayıt | +| `AllowDetail` | `false` | Satırda Detay düğmesi | +| `ConfirmDelete` | `true` | Silme onayı | +| `UseIcons` | `false` | Aksiyonlar ikon olarak | +| `NewRowPosition` | `"viewportTop"` | `first` \| `last` \| `pageTop` \| `pageBottom` \| `viewportTop` \| `viewportBottom` | +| `StartEditAction` | `"click"` | `click` \| `dblClick` | +| `SelectTextOnEditStart` | `false` | Düzenlemeye girince metni seç | +| `EditColumnName` | — | Aksiyon sütununun başlığı | +| `SendOnlyChangedFormValuesUpdate` | `false` | Update'te yalnızca değişen alanları gönder | +| `AddPageUrl` | — | Yeni kayıt popup yerine bu adreste açılır | +| `Popup` | — | Aşağıdaki alt nesne | + +`Popup` (`GridEditingPopupDto`): `Title`, `ShowTitle` (`true`), `Width` (`700`), `Height` (`500`), +`FullScreen` (`false`), `HideOnOutsideClick` (`true`), `ResizeEnabled` (`true`), +`DragEnabled` (`true`), `RestorePosition` (`true`), +`Position` (`center` \| `top` \| `bottom` \| `left` \| `right`). + +### 3.2 `EditingFormJson` (`EditingFormDto[]`) + +Düzenleme formundaki grupların listesi. Alanların hangi gruba düştüğü `ListFormField` üzerindeki +`EditGroupOrderNo` / `EditOrderNo` ile belirlenir. + +| Anahtar | Varsayılan | Anlam | +| --- | --- | --- | +| `Order` | `1` | Grubun sırası (`EditGroupOrderNo` ile eşleşir) | +| `ItemType` | `"group"` | `group` (kullanılan) \| `tabbed` \| `simple` \| `empty` \| `button` | +| `Caption` | — | Grup başlığı (dil anahtarı) | +| `ColCount` | `2` | Grup içi kolon sayısı | +| `ColSpan` | `2` | Grubun formda kapladığı kolon | + +Wizard bu listeyi `Groups` bloğundan üretir; anahtar alan ve `IncludeInEditingForm: false` +olanlar dışarıda kalır. + +--- + +## 4. Yazma komutları ve kancalar + +### 4.1 Komutlar + +| Kolon | Ne zaman | +| --- | --- | +| `InsertCommand` / `UpdateCommand` / `DeleteCommand` | Boşsa platform komutu kendi üretir; doluysa **bu SQL** çalışır | +| `InsertBeforeCommand` / `InsertAfterCommand` | Insert'ten önce/sonra | +| `UpdateBeforeCommand` / `UpdateAfterCommand` | Update'ten önce/sonra | +| `DeleteBeforeCommand` / `DeleteAfterCommand` | Delete'ten önce/sonra | + +**Kancalar, kod yazmadan iş mantığı çalıştırmanın yeridir**: stok düşme, hareket kaydı, durum +güncelleme, log, bildirim kuyruğuna satır ekleme. Karmaşıklaşınca gövdeyi bir stored procedure'e +taşı ve kancadan onu çağır (`EXEC Wms_S_AfterOrderInsert @Id, @USERID`). + +### 4.2 Komut içinde kullanılabilen anahtarlar + +| Token | Değer | +| --- | --- | +| `@ID` | Kaydın anahtarı (Delete'te seçili anahtar kümesi) | +| `@USERID` · `@USERNAME` · `@ROLES` | Oturumdaki kullanıcı | +| `@FieldName` | Ekrandan gelen o alanın değeri — tablodaki her kolon için geçerlidir (`@OrderNo`, `@Amount`) | + +`*FieldsDefaultValueJson` ile üretilen değerler (`@NOW`, `@NEWID`, `@TENANTID`, Sequence…) +komuta parametre olarak katılır (`lowcode` §4.3.5). **String birleştirme ile sorgu kurma.** + +### 4.3 `*ServiceAddress` + +`InsertServiceAddress` / `UpdateServiceAddress` / `DeleteServiceAddress` varsayılan olarak +`list-form-data/insert|update|delete` uçlarıdır. Yazma işini bir Custom Endpoint ya da Dynamic +Service'e devretmek istiyorsan bu alanı o ucun adresiyle değiştirirsin — ekranın geri kalanı +(form, doğrulama, yetki) aynı kalır. + +--- + +## 5. Ekran seviyesindeki diğer bloklar + +### 5.1 `PermissionJson` (`PermissionCrudDto`) + +Tek harfli anahtarlar: `R` (read), `C` (create), `U` (update), `D` (delete), `E` (export), +`I` (import), `N` (note). Değerler **yetki adıdır**, boolean değil: + +```json +{ "R": "App.Mrp.Orders", "C": "App.Mrp.Orders.Create", "U": "App.Mrp.Orders.Update", + "D": "App.Mrp.Orders.Delete", "E": "App.Mrp.Orders.Export", "I": "App.Mrp.Orders.Import", + "N": "App.Mrp.Orders.Note" } +``` + +Alan seviyesindeki karşılığı `ListFormField.PermissionJson`'dır ve **şeması farklıdır** +(`ListFormFieldPermissionDto`) — ekran seviyesindeki yedi harfli sözleşmeyi buraya kopyalama: + +| Anahtar | Tip | Anlam | +| --- | --- | --- | +| `Deny` | bool | `true` ise alan **hiç dönmez**; diğer anahtarlara bakılmaz | +| `C` · `R` · `U` | string | Yaratma / okuma / güncelleme yetkisinin **adı**. Boşsa ekranın kendi Create/Read/Update yetkisi kullanılır | +| `E` | bool | Alan dışa aktarımda yer alsın mı | +| `I` | bool | Alan içe aktarımda kabul edilsin mi | + +```json +{ "C": "App.Mrp.Orders.Create", "R": "App.Mrp.Orders", "U": "App.Mrp.Orders.Update", + "E": true, "I": true, "Deny": false } +``` + +`ListFormField` üzerindeki `CanRead` / `CanCreate` / `CanUpdate` / `CanExport` kolonları +**veritabanında yoktur** (`[NotMapped]`); `ListFormFieldManager.SetPermissionsAsync` bu +JSON'u oturumdaki yetkilerle karşılaştırarak istek başına hesaplar. Üçü de `false` çıkan alan +metadata'dan tamamen düşer — yani okuma yetkisi olmayan bir kolon sorguya bile girmez. +Wizard varsayılan olarak `DefaultFieldPermissionJson(permissionName)` üretir. + +### 5.2 `SubFormsJson` (`SubFormDto[]`) + +| Anahtar | Anlam | +| --- | --- | +| `TabType` | Sekmenin görünümü — `ListFormTabTypeEnum`: `List` \| `Card` \| `Tree` \| `Gantt` \| `Scheduler` \| `Todo` \| `Form` \| `Chart` \| `Pivot` | +| `TabTitle` | Dil anahtarı | +| `Code` | Hedef `ListFormCode` | +| `IsRefresh` | Ana kayıt değişince yenilensin | +| `Relation[]` | `ParentFieldName` → `ChildFieldName` + `DbType` (`System.Data.DbType`, **sayı**; bileşik anahtarda birden çok satır) | + +Alt formun kendisi de bir ListForm'dur: `IsSubForm = true` olmalı ve genellikle `CreateMenu: +false` ile menüsüz üretilir (`lowcode` §4.2). `TabType`, hedef ekranın `LayoutJson`'ında açık +olan bir görünüm olmalıdır; kapalı bir görünüm sekmede boş çizilir. + +### 5.3 `WidgetsJson` (`WidgetEditDto[]`) + +Ekranın üstündeki KPI şeridi. **Sözleşmesi sezgisel değildir, dikkatle oku:** + +| Anahtar | Ne yazılır | +| --- | --- | +| `SqlQuery` | Kartları besleyen sorgu. **Zorunludur** — boş olan widget hiç çizilmez | +| `Title` · `SubTitle` · `Value` · `Color` · `Icon` · `OnClick` | **Sorgu sonucundaki kolon adları** — sabit metin değil. Sunucu her satırda bu adlı kolonu okur; kolon yoksa boş string döner | +| `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 | + +**Bir widget, sorgunun döndürdüğü her satır için bir kart çizer.** Yani tek bir widget kaydıyla +"Bekleyen / Onaylı / Reddedilen" üçlüsü tek sorgudan üretilebilir: + +```sql +SELECT 'Bekleyen' AS Baslik, COUNT(*) AS Deger, 'amber' AS Renk, 'FaClock' AS Ikon +FROM Mrp_T_Order WHERE Status = 1 +UNION ALL +SELECT 'Onaylı', COUNT(*), 'green', 'FaCheck' FROM Mrp_T_Order WHERE Status = 2 +``` + +```json +{ "Title": "Baslik", "Value": "Deger", "Color": "Renk", "Icon": "Ikon", "ColSpan": 4, "IsActive": true } +``` + +Sorgu ekranın `DataSourceCode` bağlantısında, **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. + +### 5.4 `LayoutJson` (`LayoutDto`) + +| Anahtar | Varsayılan | +| --- | --- | +| `Grid` · `Card` · `Pivot` · `Chart` · `Tree` · `Gantt` · `Scheduler` | `true` | +| `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). + +### 5.5 `CustomJsSourcesJson` / `CustomStyleSourcesJson` + +Her ikisi de **string dizisidir**. Bir eleman `http` ya da `/` ile başlıyorsa dosya olarak +sayfaya eklenir (`