719 lines
62 KiB
Markdown
719 lines
62 KiB
Markdown
# Sozsoft Platform - Claude Instructions
|
||
|
||
This file provides Claude-specific operating rules for this repository.
|
||
Primary source of truth for platform behavior is:
|
||
|
||
- `.github/instructions/ai.instructions.md`
|
||
|
||
If there is any conflict, follow `.github/instructions/ai.instructions.md`.
|
||
|
||
## Communication Rules (apply to every response)
|
||
|
||
1. **Never introduce or summarize the platform.** Assume the user knows what this
|
||
application is, how it is built, and which technologies it uses. Do not open a response
|
||
with "Sozsoft Platform is a multi-tenant low-code engine…" or any equivalent framing.
|
||
2. **Answer the request, nothing more.** No unsolicited architecture overviews, no
|
||
restating the decision order, no re-explaining ListForm/DeveloperKit concepts unless
|
||
the user actually asked about them.
|
||
3. **No preamble, no epilogue.** Skip "Great question", "I reviewed the codebase",
|
||
summaries of what you are about to do, and closing recaps of what you just did.
|
||
4. **Match the question's size.** A yes/no question gets a sentence. A config question
|
||
gets the config. Only a full feature request gets a full proposal.
|
||
5. **Respond in the language the user writes in** (Turkish → Turkish).
|
||
6. Mention a rule from this file only when it changes the answer — never as boilerplate.
|
||
|
||
## Purpose
|
||
|
||
- Maximize delivery through runtime configuration.
|
||
- Minimize custom code.
|
||
- Preserve platform consistency, security, and tenant isolation.
|
||
|
||
Primary principle: Configuration first, code last.
|
||
|
||
## Mandatory Decision Order
|
||
|
||
For every request, evaluate in this order — internally. State the chosen step only when the
|
||
user is asking how to build something, and state it in one line, not as a walkthrough of the
|
||
options you rejected.
|
||
|
||
1. Dynamic configuration with existing ListForm ecosystem
|
||
2. SQL Query Manager + Custom Endpoint
|
||
3. Dynamic Service
|
||
4. Code change (last resort, justification required)
|
||
|
||
## Non-Negotiable Rules
|
||
|
||
1. Do not propose new custom React component/page development for standard feature requests.
|
||
2. Build new screens using platform configuration mechanisms.
|
||
3. Every implementation proposal must include tenant and permission design.
|
||
4. Never bypass platform authorization patterns.
|
||
5. Never hardcode secrets, tenant IDs, or connection strings.
|
||
6. Never add a new Core feature (`api/`, `ui/` code) without the command "core sisteme ekle" (standing default 11).
|
||
|
||
Exception:
|
||
|
||
- Custom React/backend code is written only on the user's command ("core sisteme ekle" or a
|
||
request that names the code change) — never because configuration seemed insufficient.
|
||
- When Core would be needed, say why the ListForm inventory is not enough — in a sentence or two.
|
||
|
||
## Architecture Guardrails
|
||
|
||
- Backend: Respect ABP module boundaries and explicit, auditable permissions.
|
||
- Frontend: Use existing dynamic view infrastructure and metadata-driven behavior.
|
||
- Data: Use parameterized SQL patterns and enforce tenant-safe access.
|
||
|
||
## Dynamic Platform Expectations
|
||
|
||
- Menus and routes are database-driven.
|
||
- Dynamic List/Form/Component infrastructure is the default solution path.
|
||
- Keep route, menu, permission, and datasource mappings coherent.
|
||
|
||
## Security and Compliance
|
||
|
||
- Enforce RBAC and permission-driven visibility in all layers.
|
||
- Never output real credentials, tokens, keys, or secrets.
|
||
- Use placeholders in examples.
|
||
- Maintain tenant isolation in every query and action.
|
||
|
||
## Response Contract
|
||
|
||
The list below applies **only** to a full implementation proposal for a new screen, module,
|
||
or integration. It is not a template for questions, debugging, code review, refactors,
|
||
explanations, or small changes.
|
||
|
||
Even for a full proposal: include only the items that carry real content for that request,
|
||
and drop the rest. An empty or obvious heading is noise.
|
||
|
||
1. Goal
|
||
2. Decision flow result (which step used)
|
||
3. Artifacts to configure
|
||
4. SQL/query/endpoint design (if needed)
|
||
5. Menu + route + component mapping
|
||
6. Permission and role mapping
|
||
7. Tenant isolation notes
|
||
8. Validation and test checklist
|
||
9. Rollback strategy
|
||
|
||
---
|
||
|
||
## Standing Context (read this before proposing anything)
|
||
|
||
These sections encode expectations the user will **not** repeat in every prompt.
|
||
`README.md` is the technical map of the platform; this file is how to act on it.
|
||
|
||
### Where the truth lives
|
||
|
||
| Konu | Kaynak |
|
||
| --- | --- |
|
||
| Platform davranışı, karar sırası, çıktı sözleşmesi | `.github/instructions/ai.instructions.md` |
|
||
| **Artefakt üretimi**: seed şemaları, enum değerleri, çalışan örnekler | `.github/instructions/lowcode.instructions.md` |
|
||
| **Yetenek envanteri**: JSON kolonlarının alan alan sözleşmesi, grafik/pivot/tasarımcı/mail modülü, Saas + Administration + İK menüsündeki hazır ekranlar | `.github/instructions/lowcode-reference.instructions.md` |
|
||
| `api/` kod standardı (sürüm, nullable, ölü kod, modern C#, ABP katmanları) | `.github/instructions/dotnet.instructions.md` |
|
||
| Modül/liste ekleme prosedürü, seeder dosya sorumlulukları | `.github/instructions/list.instructions.md` |
|
||
| Ne nerede yaşıyor, hangi ekran ne yapıyor | `README.md` |
|
||
| Bir modülün nasıl çalıştığı, analizi ve kararları | `configs/seeds/docs/modules/{Menü}.md` (dizin: `README.md` §10.5) |
|
||
| Yetki kodları | `PlatformConsts.AppCodes` (backend) + `constants/permission.constant.ts` (UI) |
|
||
| Seed yolları | `SeedPathResolver` |
|
||
| SQL Server → PostgreSQL çeviri kuralları | `.github/instructions/lowcode.instructions.md` §8.3.1 |
|
||
|
||
Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` → bu dosya → `README.md`.
|
||
|
||
### Standing defaults — do not ask again
|
||
|
||
1. **Yeni tablo** → tenant + tam audit kolonları varsayılan gelir (`Id`, `TenantId`,
|
||
`CreationTime`, `CreatorId`, `LastModificationTime`, `LastModifierId`, `IsDeleted`,
|
||
`DeletionTime`, `DeleterId`). Kullanıcı açıkça "tenant yok / audit yok" demedikçe çıkarma.
|
||
Tanım **olmayan** (işlem/belge) yeni tablo `TenantId` gibi `BranchId uniqueidentifier NOT NULL`
|
||
(PG `uuid NOT NULL`) da taşır; ekranı `IsBranch: true`. **Şube alanı her ekranda aynı sözleşmeyle
|
||
gelir** (`BranchId` kolonu taşıyan tanım ekranları dahil): seçenekler kullanıcıya atanmış aktif
|
||
şubelerdir (`Sas_T_BranchUsers`, Kullanıcılar › Yetkiler › Şube Yönetimi; `LookupQueryValues.UserBranchValues`),
|
||
yeni kayıt bu listenin **ilk seçeneğiyle** açılır (`DbQuery`, `LookupQueryValues.UserDefaultBranchValue`
|
||
ile aynı metin). Departman şubesi, sabit şube ya da ekrana özel sorgu yazılmaz.
|
||
Bu tablodan okuyan view/rapor ekranı da `BranchId`'yi taşır ve `IsBranch: true` olur. Ana kayda
|
||
bağlı tablo da kolonu taşır; formda önce Şube seçilir, ana kayıt lookup'ı şubeye göre süzülür
|
||
(`CascadeParentFields: BranchId`) + alt form ilişkisi `BranchId → BranchId`. Ayrıntı:
|
||
`lowcode.instructions.md` §8.1 (İşlem tablosu → `BranchId`).
|
||
Yeni **tanım** tablosu (tanım olup olmadığına talebin içeriğine göre analizde karar verilir;
|
||
sabit ad listesi yoktur) buna ek olarak `IsActive bit NOT NULL DEFAULT 1` ile gelir; liste + form alanı, varsayılan
|
||
`true`, Aktif/Pasif widget'ı. Tanım **olmayan** tabloya (işlem/belge tablosu, onun alt formları:
|
||
satır, malzeme, süre, duruş, not) `IsActive` eklenmez; yalnızca talep aktif/pasif bayrağını açıkça
|
||
isterse aynı varsayılanla gelir: tabloda `NOT NULL DEFAULT 1` (`create_table`'da
|
||
`isNullable: false`, `defaultValue: "1"`), formda yeni kayıt `true` ile açılır
|
||
(`FormFieldsDefaultValueJson`). Ayrıntı: `lowcode.instructions.md` §8.1.
|
||
Görsel tutan her kolon (fotoğraf, logo, avatar kopyası) `nvarchar(MAX)` + editör `dxImageUpload`;
|
||
değer base64 saklanır, sınırlı boy ve `dxImageViewer` kullanılmaz (deploy ikisini de reddeder).
|
||
2. **Yeni ekran** → Wizard yolu. `Definitions` altına otomatik yerleştirme yok; modüle ait yeni
|
||
bir kök menü oluştur, `Order = max(Order) + 1`. Yetki ağacı menü ağacını izler: ekranın okuma
|
||
yetkisi menüdeki dalın ara yetkisinin altına yazılır; menüsüz alt formun yetkisi ise listenin
|
||
yetkisine bağlanır (`ParentName` = listenin yetki kodu) (`lowcode.instructions.md` §0.12 Menü yeri).
|
||
3. **Yeni ekran/menü** → yetki sözleşmesi olmadan önerilmez. Menü ve route aynı ekran
|
||
sözleşmesini göstermeli.
|
||
4. **Kullanıcıya görünen her metin** → dil anahtarı üzerinden, EN + TR birlikte. Koda gömülü
|
||
metin önerme.
|
||
**Kullanıcıya görünen her tarih ve sayı** → aktif kültürle: UI'da `localeFormat.ts`
|
||
(`formatLocaleDate/Time/DateTime/Number/Currency/Fixed`), API'de `CultureFormat` + `CultureInfo.CurrentCulture`;
|
||
DevExtreme kolon/editörü adlı biçim kullanır (`shortDate`, `shortDateShortTime`, `shortTime`, `decimal`,
|
||
`fixedPoint`, `currency`). Sabit desen (`DD.MM.YYYY`, `dd/MM/yyyy`), `'tr-TR'` sabiti, `toLocaleString()` ve
|
||
görüntülenen ondalık için `toFixed` yazılmaz; makine biçimi (ISO yük, `<input type=date>`) `ui/README.md`'de.
|
||
5. **Runtime'da üretilen her artefakt** (wizard, custom component, crud endpoint) → ilgili seed
|
||
dosyası da yazılır/güncellenir. Veritabanı sıfırlandığında geri gelmeyen bir şey üretme.
|
||
6. **Yeni ekran/komponent talebi — yazılı ya da görsel** → dosya yazmadan önce **keşif turu**:
|
||
toplu sorularla (tur başına en fazla 4, seçenekli, önerilen varsayılanlı) ekran türü, veri
|
||
kaynağı, modül, kapsam ve yolun soru setini al; tarifi kur ve **onaylat**, sonra
|
||
`configs/seeds/` altına üret. Şüphede Wizard. Kullanıcı "sorma / sen seç" derse
|
||
varsayılanlarla üret ve sonunda listele. Depodan okunabilen şeyi sorma; tabloya yazma hedefi,
|
||
onaycı ve hesap formülü asla varsayılmaz. Ekran görüntüsü piksel sözleşmesi değildir;
|
||
görsel Tur 2'nin çoğunu cevaplar, yalnızca görselden okunamayanlar sorulur.
|
||
Bu kural yalnızca **yeni üretim** içindir; düzeltme/alan ekleme/debug doğrudan yapılır.
|
||
Ayrıntı: `lowcode.instructions.md` §0.6–§0.7.
|
||
7. **Ziyaretçiye açık yeni yol** (public `RoutePath` taşıyan custom component, `Pub` menüsü,
|
||
yeni public sayfa) → SEO alanları da doldurulur. Ayrı bir SEO tablosu yoktur: meta, yolu
|
||
tanımlayan kaydın `Seo*` kolonlarındadır — `data/Sas.Menus.Routes.json` ya da
|
||
`data/Sas.DeveloperKit.CustomComponents/{Id}.json`. Metin `::Anahtar` ile dil anahtarı taşır.
|
||
Kullanıcı SEO bilgisi girmez; üreten yazar. Genel varsayılanlar kayıtta değil
|
||
`Adm.SiteManagement.Seo.*` ayarlarındadır. Ayrıntı: `lowcode.instructions.md` §0.9.
|
||
8. **Ekran içi hesap/koşul ihtiyacı** → önce `EditorScript` (Script Builder tarifleri), sonra
|
||
`EditorOptions`; kod yazmak son çare.
|
||
9. **SQL** → her zaman parametreli. String birleştirme ile sorgu kurma.
|
||
10. **Silme** → `DeleteCommand = DefaultDeleteCommand("{Tablo}")`; ham SQL string'i yazma.
|
||
11. **Core'a yeni özellik yalnızca komutla** → Core = `api/` + `ui/` kaynak kodu; çerçeve =
|
||
`configs/seeds/` artefaktları. Kullanıcı **"core sisteme ekle"** (ya da Core değişikliğini adıyla
|
||
isteyen eşdeğer komut) demedikçe Core'a yeni özellik yazılmaz — küçük/geriye dönük uyumlu olması
|
||
fark etmez: DTO alanı, var olan bileşene prop/olay, tarif, `EditorOptions` anahtarı, token, yeni
|
||
React bileşeni/sayfası, çekirdek motor, entity/migration, platform tablosu şeması, paket,
|
||
`Routes`, dış entegrasyon. "Core gerekiyor" demeden önce ListForm'un bütün yetenekleri taranır
|
||
(`lowcode-reference.instructions.md` §0–§9, `lowcode` §4–§9). Komut yoksa çerçeve içindeki kısım
|
||
bitirilir, son mesajda tek bir "Core gerektiren kısım" notu yazılır (soru kutusuyla Core önerilmez).
|
||
Bildirilen hatanın yeni ayar eklemeden düzeltilmesi yeni özellik değildir. Komut yalnızca adı
|
||
geçen değişikliği kapsar, sonraki talebe taşınmaz; eklenen yetenek envantere yazılır. "Yapılamaz"
|
||
cevabı yoktur. Ayrıntı: `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.
|
||
13. **Liste ekranı widget'ları** → Wizard ya da MCP `deploy_wizard` ile üretilen her List ekranı
|
||
sorulmadan **en az 3 kartlık** `Widgets` ile gelir; kartlar tablonun durum / boolean bayrak /
|
||
tarih / tutar kolonlarından seçilir, başlıklar dil anahtarıdır (`Sas.Widget.*` önce). Kiracı
|
||
tablosunda her kart `"TenantId" = '@TENANTID'` taşır; `IsDeleted` koşulu yazılmaz. Form/Chart,
|
||
alt form ve basit tanım listeleri istisnadır. **Alt form oluşturulurken widget oluşturulmaz**:
|
||
başka bir ekranın `SubForms` sekmesi olarak üretilen her ekran — menüsüz ya da kendi menüsü olan —
|
||
`Widgets: []` ile deploy edilir. Deploy menüsüz (`CreateMenu: false`) wizard'ın `Widgets`'ını
|
||
ayrıca düşürür; ekran alt form sekmesinde açıldığında şerit de çizilmez
|
||
(`useListFormWidgets(…, isSubForm)`) — kart sorgusu ana kayda göre süzülmez. Ayrıntı:
|
||
`lowcode.instructions.md` §4.6.1.
|
||
Alt form ekranının menü/yetki/düzenleme kuralları (menüsüz alt formun yetkisi ana ekranın altında,
|
||
`Form` sekmesi tek kayıt + `AllowAdding`/`AllowUpdating` açık): `lowcode.instructions.md` §4.6.
|
||
14. **Modül dokümanı** → MCP ya da wizard ile oluşturulan her modül çalışma şeklini
|
||
`configs/seeds/docs/modules/{Menü}.md` dosyasında taşır: dosya kök menü başına tektir ve adı kök
|
||
menünün kodudur (`Hr.md`, `Prc.md`, `Adm.md`),
|
||
menünün altındaki her dal dosyada bir `module-doc:section` bölümüdür. Bölüm keşif cevapları ve
|
||
onaylanan tarifi (analiz), ekranları, veri modelini, hesapları/durum geçişlerini, yetki dağılımını,
|
||
entegrasyonları, sık sorulanları taşır. Dosyayı, dalın bölümünü ve "Ek — Ekran envanteri"ni deploy
|
||
kendisi açar/yeniler; anlatım alt bölümlerini (`module-doc:todo`) üreten doldurur — MCP'de
|
||
`save_module_doc` (ekran/dal koduyla yalnızca o bölüm), Claude Code'da dosyaya doğrudan. MCP talebi
|
||
bölümü tamamlanmadan `Testing`'e alınamaz. Wizard dışı modülün bölümü elle yazılır. README'ye
|
||
yalnızca §10.5 dizin satırı/bölüm adı ve ilgili bölümde tek cümlelik bağlantı yazılır.
|
||
"X nasıl çalışıyor?" sorusunda önce bu dosya okunur. Ayrıntı: `lowcode.instructions.md` §0.13.
|
||
15. **Ekran açıklaması** → wizard/MCP ile üretilen her liste ekranının (alt formlar dahil)
|
||
`DescTr`/`DescEn` metni ayrıntılıdır; başlıktaki ⓘ simgesinde gösterilir: ne işe yaradığı,
|
||
menüde nerede / hangi ekranın sekmesi olduğu, ana alanlar, otomatik hesap ve geçişler, ilişkili
|
||
ekranlar (`RichText` biçimi). MCP `deploy_wizard` 3 satır / 150 karakterin altını reddeder.
|
||
Ayrıntı: `lowcode.instructions.md` §4.4.2.
|
||
16. **İki veritabanı sağlayıcısı** → uygulama SQL Server ile olduğu gibi PostgreSQL ile de çalışır
|
||
(`SettingsConsts.DefaultDatabaseProvider`). Veritabanına dokunan her değişiklik ikisi için birlikte
|
||
düşünülür ve yazılır; kullanıcı bunu her istekte hatırlatmaz:
|
||
- `configs/seeds/sql/{object,execute,host/…}/X.sql` yazılan/değişen her dosyanın
|
||
`configs/seeds/postgres/…/X.sql` eşi aynı işte yazılır/güncellenir (aynı ad, aynı parametre sırası,
|
||
şema PG'de `public`). Biri olmadan diğeri commit edilmez; "PG sonra" yoktur.
|
||
- EF modeli SQL Server sözdizimiyle yazılır (kolon tipi, index filtresi, check, varsayılan); PG
|
||
karşılığı `PostgreSqlModelSyntax` üzerinden `PlatformDbContext.ConfigureProviderSpecificModel`'de
|
||
üretilir. Çeviriye girmeyen yeni bir sözdizimi eklenirse çeviri de eklenir.
|
||
- C#'ta üretilen SQL (Dapper, `QueryManager`, seeder komutları, ekran `*Command`/`LookupQuery`
|
||
metinleri) ya iki sağlayıcıda da çalışan tırnaklı/ANSI biçimde yazılır ya da `DataSourceType`'a göre
|
||
dallanır.
|
||
- SQL değişikliği doğrulanırken PG eşi de bir PostgreSQL veritabanında çalıştırılır (iki kez —
|
||
idempotent). Ayrıntı ve dönüşüm tablosu: `lowcode.instructions.md` §8.3.1.
|
||
17. **Her prompt sonunda optimizasyon + ölü kod temizliği** → kullanıcı "optimize et, ölü kodları
|
||
kaldır" yazmaz; her iş bitmeden, kullanıcı istemese de o işte dokunulan kod üzerinde yapılır:
|
||
kullanılmayan `using`/üye/parametre/tip/dil anahtarı silinir (tip silmeden önce repo geneli
|
||
tarama, "Definition of done"), aynı mantığın ikinci kopyası ortak metoda indirilir, gereksiz
|
||
tekrar sorgu/dosya okuması kaldırılır, kod/değer **statik yazılmaz** — tablo adı
|
||
`TableNameResolver`'dan, değer tablo/seed/ayar verisinden dinamik bulunur. Son mesajda ne
|
||
temizlendiği tek satırla söylenir.
|
||
Kapsam seed artefaktlarını da içerir: yeni bir yordam/view/ekran eskisinin işini devralınca eskisi
|
||
aynı işte silinir (iki sağlayıcının dosyası, wizard'ı, menü/yetki/dil satırları). Kullanımı biten
|
||
SQL nesnesi (tablo, view, yordam, fonksiyon), menüsüz ve referanssız wizard, ekranı olmayan `data/`
|
||
dosyası ve hiçbir ekran öğesinin göstermediği dil anahtarı bırakılmaz; tarama kuralı
|
||
`lowcode.instructions.md` §8.6.
|
||
18. **Yeni kaydın form varsayılanı** → Wizard ve MCP ile üretilen her listede form varsayılanı
|
||
kolonun veritabanındaki sabit `DEFAULT` değeridir; deploy onu tablodan (katalog ya da seed
|
||
scripti) okuyup `FormFieldsDefaultValueJson`'a yazar, değer koda gömülmez. Bu yüzden tablo
|
||
şu `DEFAULT`'larla kurulur: `IsActive` → `1` (true), ölçü birimi (`*UomId`) → `'Adet'`, para birimi
|
||
(`*CurrencyId`/`*Currency`) → `'TRY'` (lookup'ın anahtarı neyse); talepte başka sabit varsayılan
|
||
varsa (Süre = 60) o da kolonun `DEFAULT`'udur. Alana özel değer wizard öğesinin `DefaultValue`'su ile
|
||
verilir. Ayrıntı: `lowcode.instructions.md` §4.3.5.1.
|
||
19. **Sabit lookup seçenekleri** → RadioGroup/SelectBox/TagBox/Autocomplete'in `StaticData` seçeneklerinde
|
||
`name` düz metin değil `Sas.StaticLookup.*` dil anahtarıdır; EN + TR satırı dil dosyasına yazılır.
|
||
Deploy düz metni kendisi anahtara çevirir (aynı TR metinli anahtar varsa onu kullanır) —
|
||
`ListFormWizardAppService.LocalizeStaticLookupsAsync`, `lowcode.instructions.md` §4.3.4.1.
|
||
20. **EditorScript** → MCP/wizard script'i elle yazmaz, Script Builder kurallarını verir; deploy kodu
|
||
kurallardan üretir (`EditorScript.Rebuild`). `str/num/bool` alan **adı** alır (`str('X')`, değeri
|
||
değil). `setValue`'da `true`/`false` mantıksal yazılır (onay kutusu alanında `0`/`1` deploy'da çevrilir).
|
||
MCP ve Script Builder aynı kuralları aynı koda çevirir; `set` zinciri derinlik sınırsızdır, döngü ve
|
||
script hatası kullanıcıya bildirilir. Ayrıntı: `lowcode.instructions.md` §4.4, §5.3, §5.6.
|
||
21. **Demo veri ≠ seed senkronu** → `SeedData: true` yalnızca `SeedFilePath` açar;
|
||
`SeedSyncInsert/Update/Delete` istekte açıkça istenmedikçe `false`'tur (wizard cevabı `SeedSync`).
|
||
22. **Wizard adı modül önekli** → yeni wizard'ın adı (= seed dosyasının adı) `{ModuleId}_{Ad}`'dır
|
||
(`Prc_RejectReason.json`, `Hr_RejectionReason.json`); aynı ekran adı başka modülde de geçebildiği
|
||
için öneksiz ad dosya çakışması (`'X' adında bir Wizard zaten var`) üretir. Önek deploy'da
|
||
`MenuParentModuleId`'den eklenir (`ListFormWizardDto.ResolveWizardName`); analiz ve üretim planında
|
||
dosya adı önekli yazılır. `configs/seeds/wizard/` altındaki bütün dosyalar bu düzendedir; öneksiz
|
||
dosya yalnızca eski bir export arşivinden gelebilir ve düzenlenirken adı korunur. Ayrıntı:
|
||
`lowcode.instructions.md` §4.10 (Adım 2 — Menü).
|
||
23. **Otomatik numara Sequence'tan** → wizard/MCP üretiminde talep otomatik numara istediğinde ya da
|
||
analiz bir alanın (`ProjectNo`, `OrderNo`, `RequestNo` …) otomatik numaralanmasına karar verdiğinde
|
||
alan `Adm.Definitions.Sequence` numaratörüne bağlanır; elle girilen numara/kod alanı bu kurala
|
||
girmez. Numaratör
|
||
`data/Adm.Definitions.Sequence.json`'a satır olarak yazılır (uygun olan varsa yeniden kullanılır),
|
||
alanın varsayılanı `FormFieldsDefaultValueDto`'da `CustomValueType: 5`, `Value` = numaratörün `Name`'i.
|
||
`@AUTONUMBER`, `MAX + 1` sorgusu ya da SQL ile numara üretilmez (boşluksuz numara açıkça istenmedikçe).
|
||
Ayrıntı: `lowcode.instructions.md` §4.3.5.2.
|
||
24. **İş akışlı liste** → tablo, tasarımcının Workflow şablonundaki dört kolonla kurulur ve `WorkflowDto`
|
||
yalnız bunlara bağlanır: `ApprovalUserName` nvarchar(256), `ApprovalStatus` nvarchar(50),
|
||
`ApprovalDate` datetime, `ApprovalDescription` nvarchar(200) (PG: varchar/timestamp). Ayrı durum
|
||
kolonu (`Status`), Guid onaycı kolonu (`ApproverId`) ve red nedeni kolonu açılmaz: durum akışın
|
||
düğüm başlığıdır, red nedeni onay düğümünün `RejectReason` listesinden seçilip açıklamaya yazılır.
|
||
Bu dört alan popup düzenleme formunda yer almaz (akış yazar), listede kolon olarak görünür.
|
||
Ayrıntı: `lowcode.instructions.md` §4.7.
|
||
25. **Not yalnızca Table kaynağında** → kayıt kaynağı (`SelectCommandType`) Table olmayan listede (View, Query,
|
||
StoredProcedure, TableValuedFunction) Not yoktur: `PermissionJson.N` boş, `ShowNote` kapalı, `{Kod}.Note` yetki satırı ve
|
||
satırdaki Not butonu üretilmez; view/sorgu satırının notun bağlanacağı kalıcı kaydı yoktur. Wizard/MCP bunu kendisi uygular
|
||
(`ListFormWizardDto.HasNote`), C# seeder'da `DefaultPermissionJson(name, hasNote: false)` yazılır. Ayrıntı:
|
||
`lowcode.instructions.md` §4.2 (Üretilen yetkiler).
|
||
26. **Özel komutun parametreleri boş bırakılmaz** → MCP/prompt/wizard ile üretilen ya da düzenlenen ekranda yazılan her
|
||
`InsertCommand`/`UpdateCommand`/`DeleteCommand` ve `*BeforeCommand`/`*AfterCommand` ile aynı işte
|
||
`Insert/Update/DeleteFieldsDefaultValueJson` da tamamlanır: komuttaki her `@Parametre` gönderilen alana, `IsTenant`'ın
|
||
eklediği `@TenantId`'ye ya da listedeki bir satıra (`Id=@ID`, `LastModifierId=@USERID`, `DeleterId=@USERID`,
|
||
`CreatorId=@USERID`, `*Time=@NOW`) bağlanır; kaynağı olmayan parametre "Must declare the scalar variable" verir.
|
||
Wizard'ın audit varsayılanları view/sorgu kaynaklı ekranda ve özel komutta eksik kalır, bu yüzden kontrol üretendedir.
|
||
`SelectFieldsDefaultValueJson` çalışma anında okunmaz; okuma koşulu `DefaultFilter`'a yazılır. Ayrıntı:
|
||
`lowcode.instructions.md` §4.3.5.3.
|
||
|
||
### Cross-cutting: touching one thing means touching these too
|
||
|
||
- **Modül dokümanı** → `ModuleDocManager.RefreshAllAsync` wizard'ı yazan her yoldan çağrılır (deploy,
|
||
silme, `WizardSeedSynchronizer`, import kapanışı); wizard dosyasını yazan yeni bir yol eklenirse
|
||
çağrı da eklenir, yoksa envanter bayatlar. Wizard → doküman/bölüm eşlemesi tek yerdedir
|
||
(`ModuleDocCatalog`: dosya = kök menü, bölüm = beyan → alt formun ana ekranı → menü dalı; kök ve
|
||
dal `data/Sas.Menus.Menu.json`'dan okunur, sabit liste yoktur); envanter biçimi
|
||
`ModuleDocRenderer`'dadır. Export kapanışı dokümanı taşır (`WizardExportCollector`), import
|
||
`docs/modules/*.md` yolunu kabul eder.
|
||
|
||
- **`scriptRecipes.ts` (TS) ↔ `Domain.Shared/Editors/*.cs`** — biri değişirse diğeri de değişir.
|
||
İkisinin çıktısı **birebir aynı** olmak zorundadır; aksi hâlde seeder ile basılan script
|
||
dialogda "elle düzenlenmiş" sayılır ve kural editörü kapanır.
|
||
- **`phoneFormat.ts` ↔ `DynamicReportPhoneFormatter.cs`** — telefon token'larının (`phoneGlobal`, `phoneTR`,
|
||
`phoneIntl`, `phoneCountry`) görüntü kuralı iki yerde yazılır: liste/kart istemcide, dinamik rapor sunucuda.
|
||
Biri değişirse diğeri de değişir; yoksa liste ile rapor aynı numarayı farklı yazar.
|
||
- **`Sequence.GetNextSequence` (C#) ↔ `Adm_P_SequenceNext.sql` (SQL Server + PG)** — numaratörün biçim
|
||
(`FormatTemplate` token'ları, `PaddingSize`) ve sıfırlama (`ResetPeriod`/`LastPeriodKey`) kuralı iki yerde yazılır:
|
||
formdan açılan kayıt C#'tan, yordamın açtığı belge (tekliften sipariş, alternatif teklif) SQL'den numara alır.
|
||
Biri değişirse diğeri de değişir; yoksa aynı numaratör iki farklı biçimde numara üretir. Eşzamanlılık korumaları
|
||
ayrıdır ve ikisi de kalır: yordam satır kilidi (`UPDLOCK`/`FOR UPDATE`), C# karşılaştır-ve-yaz
|
||
(`ISequenceRepository.TryAdvanceAsync`, `SequenceManager` yeniden dener) — entity izlenerek okunup `UpdateAsync`
|
||
ile yazılmaz, iki form aynı numarayı alırdı.
|
||
- **Liste popup'ı (Grid/Tree) ↔ form sayfası (`FormDevExpress`)** — editör yetenekleri ortak yardımcılardadır
|
||
(`list/shared/editingForm.ts`, `editingFormScripts.ts`, `cascade.ts`); TagBox/GridBox/ImageUpload/ImageViewer/Rating'in
|
||
tek uygulaması `form/editors/*`, liste hücresi yalnızca `list/editors/cellEditors.tsx` adaptörüdür. Bir tarafa
|
||
yetenek eklenirken ortak yardımcıya eklenir; tek tarafa yazılan yetenek eşliği bozar. Tablo:
|
||
`lowcode.instructions.md` §4.3.4.
|
||
- **`htmlEditorOptions.ts` ↔ `EditorOptions.Html`** — HtmlEditor varsayılanı iki yerde yazılır ve
|
||
bloklar birebir aynıdır. İstemci iç içe blokları tek seviye birleştirdiği için seed edilen
|
||
`toolbar` varsayılanın yerine geçer; biri değişip diğeri değişmezse seed'li ekranlar eski araç
|
||
çubuğunu gösterir.
|
||
- **Wizard seed dosyası tek `Wizard` bölümüdür** → dosyada `ListForm`/`Fields` bölümü yoktur; ekranın
|
||
bütün sözleşmesi `Wizard` bölümündedir. ListForm detayının her sekmesi orada bir bloktur (`Editing`,
|
||
`Insert`/`Update`/`Delete`, `FormDefaults`, `Permission`, `FilterRow` … `PivotOption`, görünüm option
|
||
blokları, `ExtraFilters`, `CommandColumns`, `SubForms`, `Widgets`, `WorkflowDto`, `ChartOptions`), alan
|
||
detayının her sekmesi `Groups[].Items[]` öğesinin bir alanıdır (`WizardColumnItemInputDto`: `Filter`,
|
||
`Header`, `Grouping`, `TotalSummary`/`GroupSummary`, `Join`, `ValidationRules`, `Styling`, `Permission`,
|
||
`PivotSettings`, `ListOrderNo` …). Wizard alanları `Menu`, `Language`, `Data`, `Details`, `Layout`
|
||
başlıklarına toplanır ve `ListFormCode` → `WizardCode`, `MenuParentModuleId` → `ModuleId`,
|
||
`LanguageTexts` → `Language.Texts` adıyla yazılır; varsayılanı olan alanlar (`CreateMenu: true`, boş
|
||
`DataSourceConnectionString`, boş `MenuParentPath` / `LanguageTexts`) hiç yazılmaz. Kısayol cevaplar
|
||
(`AllowAdding/AllowUpdating/AllowDeleting/AllowDetail/OpenDetailAfterInsert/ConfirmDelete`,
|
||
`SeedData`/`SeedSync`, öğedeki `IsRequired`) **dosyaya yazılmaz**: `Editing`, `Data.SeedFilePath`/`SeedSync*`
|
||
ve `ValidationRules`'tan türetilir (`SyncDerivedAnswers`); deploy girdisinde yazılırsa bloğu ezer. Bu yalnızca
|
||
dosya biçimidir; C# sözleşmesi düzdür, dönüşüm `WizardSeedFileDto.ToSeedJson` / `FromSeedJson` içindedir.
|
||
**Wizard seed dosyası her zaman bu iki metotla okunup yazılır**; düz `JsonSerializer` çağrısı başlıkları
|
||
ve türetilen kısayolları sessizce bozar. `FromSeedJson` eski biçimin `ListForm`/`Fields` bölümlerini
|
||
okuyup `RefreshFrom` ile `Wizard`'a aktarır; bir sonraki yazımda düşerler.
|
||
- **Wizard ile üretilen ekran** → ListForm detayından yapılan her kayıt (`ListFormsAppService`,
|
||
`ListFormFieldsAppService`, `ListFormJsonRowAppService`) seed dosyasının `Wizard` bölümünü
|
||
`ListFormWizardDto.RefreshFrom(listForm, fields)` ile geri yazar (`WizardSeedSynchronizer`). Bu üçlüden
|
||
birine yeni bir kayıt yolu eklenirse senkronizasyon çağrısı da eklenir; yoksa dosya bayatlar.
|
||
**Eşlik kuralı:** `GridOptionsEditDto`/`ColumnFormatEditDto`'ya eklenen her alanın
|
||
`ListFormWizardDto`/`WizardColumnItemInputDto`'da karşılığı + `RefreshFrom` + `BuildListForm/BuildFields`
|
||
eşlemesi olmak zorundadır; yoksa tasarımcıda yapılan değişiklik migrate'te kaybolur.
|
||
- **Wizard girdisinden ekran üretimi** → tek hat `ListFormWizardDto.ApplyDefaults(isDeleted, isCreated,
|
||
isModified)` (boş blokları `WizardDefaults` + `WizardConsts` ile doldurur; yetki adları, popup başlığı,
|
||
durum anahtarı ekran kodundan yeniden türetilir) → `BuildListForm()` / `BuildFields()` (salt eşleme,
|
||
parametresiz) → `RefreshFrom` (üretilen ekran cevaplara geri yazılır, dosya öyle kaydedilir); entity'ye
|
||
yazma `GridOptionsEditDto.ApplyTo` / `ColumnFormatEditDto.ApplyTo`. Deploy, seeder (`WizardDataSeeder`
|
||
dosyadan `Wizard.BuildListForm()/BuildFields()`) ve senkronizasyon aynı metotları çağırır; ikinci bir
|
||
üretim kopyası yazma.
|
||
- **Katman sınırı (DDD)** → `DbMigrator` Application'a referans vermez. Seeder ile AppService'in
|
||
paylaştığı dönüşüm `Application.Contracts`'ta DTO metodudur; entity'ye yazma Domain.Shared'deki
|
||
`IListFormArtifact` / `IListFormFieldArtifact` sözleşmeleri üzerinden yapılır.
|
||
Ayrıntı: `dotnet.instructions.md` §5.
|
||
- **Yeni bir yetki** → `PlatformConsts.AppCodes` + `data/Abp.Identity.Permissions.json` + (UI'da kullanılacaksa)
|
||
`permission.constant.ts` + `data/Sas.Languages.LanguageText.json`. Yetki ekranındaki sıra `AbpPermissions.Order`
|
||
(grup için `AbpPermissionGroups.Order`) sütunundan gelir ve seed satırındaki `Order` alanıyla
|
||
yazılır; sıra kardeşler arasındadır — her üst yetkinin altında 1'den başlar, yeni satırı menüdeki
|
||
yerine denk gelen numarayla ekle.
|
||
- **`/mcp` ucuna yeni bir araç** → üç yer birlikte değişir: `McpToolNames` (ad kayıt defteri),
|
||
ilgili `Mcp*Tools` sınıfı (`Sozsoft.Mcp.HttpApi`) ve arkasındaki AppService sözleşmesi
|
||
(`Sozsoft.Mcp.Application.Contracts`). Arac adı `McpToolNames.All` içinde yoksa araç listesi
|
||
süzgecinden geçmez ve hiç görünmez; MCP Servers ekranındaki `AllowedTools` seçenekleri de
|
||
`SelectableTools`'dan üretildiği için kendiliğinden güncellenir. Sunucu kaydında tek anahtar
|
||
`AllowedTools`'tur: boşsa tüm araçlar, doluysa yalnızca seçilenler açıktır; araç tipine göre ayrı
|
||
bir bayrak yoktur, sınırı kullanıcı yetkisi çizer. **İki MCP yetkisi vardır:** `.Invoke` uca
|
||
erişim ve okumadır (MCP servislerinin sınıf düzeyinde aranır); `.Write` MCP üzerinden yapılan her değişikliktir ve
|
||
değişiklik yapan her araç `RequiredPermissions`'ta `.Write`'a bağlanır — okuma/yazma ayrımı için
|
||
yeni yetki açılmaz. **Kiracı veri okur ve yazar, geliştirme yapamaz**: kiracı bağlamında açık
|
||
araçlar `McpToolNames.TenantTools` ile sınırlıdır (şema, `query_list`, `run_select`,
|
||
`insert_row`/`update_row`/`delete_row`) ve `AllowedTools` bu kümeyi yalnızca daraltır. Satır
|
||
yazma ekranın C/U/D sözleşmesinden geçtiği için kiracıya açıktır; ekran yetkisinden geçmeden
|
||
yazabilen araç (procedure, Custom Endpoint) ve geliştirme araçları (`DevelopTools`) `TenantTools`'a
|
||
eklenmez. Geliştirme işlemi yine Wizard / Custom Components AppService'inden geçer.
|
||
**`/mcp` istek düzeyindeki unit of work'ün dışındadır** (`AbpAspNetCoreUnitOfWorkOptions.IgnoredUrls`):
|
||
cevap SSE ile aktığı için istek UoW'si sonuç istemciye gittikten sonra commit edilir ve bağlantı
|
||
kapanınca geri alınır — araç başarılı görünür, EF kayıtları kaybolur. Her MCP servis çağrısı
|
||
kendi UoW'sini sonuç dönmeden tamamlar; bu istisna kaldırılmaz.
|
||
Kapı iki yerdedir: `AddListToolsFilter` listeyi süzer, `McpAppService.EnsureToolAllowedAsync`
|
||
çağrıyı reddeder — süzme tek başına yetki sınırı değildir, ikisi birlikte yazılır. `Invoke`
|
||
uçta değil her MCP servisinin sınıf düzeyindeki `[Authorize]`'dadır; yeni bir MCP AppService'i de
|
||
bu attribute'la açılır.
|
||
**MCP'nin kendi reddi istisna değildir, modelle konuşmadır**: eksik/hatalı girdi, durum kuralı,
|
||
kapalı araç ve eksik yetki `McpProblem` olarak döner — MCP AppService'leri `McpOutcome<T>` /
|
||
`McpProblem?` döndürür, domain servisleri (`McpSqlGuard`, `McpTableCreator`, `McpSqlObjectWriter`)
|
||
de öyle; araç katmanı bunu `McpToolResult.From` ile hata sonucuna (`CallToolResult.IsError`) çevirir,
|
||
model mesajı okuyup tekrar dener. `throw new UserFriendlyException` ve `AuthorizationService.CheckAsync`
|
||
MCP kodunda kullanılmaz (debugger'ı durdurur, SDK'da yığın izli `ERR` üretir); yetki
|
||
`McpAppService.CheckPermissionAsync` ile adıyla istenir. İstisna yalnızca gerçek hatadır (kod hatası,
|
||
beklenmeyen durum). Arkadaki platform servisinin (wizard, custom component, ListForm verisi) attığı
|
||
`UserFriendlyException`/yetki reddi ile sınıf düzeyindeki `[Authorize]` reddi `McpToolErrorTranslator`
|
||
(çağrı filtresi) ile mesaja çevrilir. Yeni aracın arkasındaki servis bir yetkiyi `[Authorize]`
|
||
attribute'uyla istiyorsa (ret mesajı yetki adını taşımaz) o yetki `McpToolNames.ServicePermissions`'a
|
||
eklenir; ekran yetkisi gereken araç yetkiyi `IMcpListFormAccessFilter.GetPermissionNameAsync` +
|
||
`CheckPermissionAsync` ile adıyla önceden ister.
|
||
Araç → yetki tablosu `README.md` §13.9'dadır; araç ya da yetki değişirse tablo da değişir.
|
||
Hazır prompt'lar (`McpPromptNames` + `McpDevelopPrompts`) yalnızca metindir; adımları tekrar
|
||
etmez, `get_dev_guide` bölümüne yönlendirir. Bir kural bağlayıcıysa prompt'a değil araca yazılır
|
||
(örnek: `Ready to Start` şartı `start_dev_request`'tedir).
|
||
- **Wizard deploy veritabanına yazmaz** → wizard ekranı, MCP `deploy_wizard` ve düzenleme aynı
|
||
yoldan (`ListFormWizardAppService.Create`) geçer ve **yalnızca seed dosyası yazar**: `wizard/{Ad}.json`
|
||
+ menü/modül/izin grubu/yetki/dil satırları (`data/Sas.Menus.{Menu,Module}.json`,
|
||
`data/Abp.Identity.Permission{Groups,s}.json`, `data/Sas.Languages.LanguageText.json`;
|
||
`WizardSeedRowStore`, anahtarlar `WizardSeedRowKeys`). Kurulum yalnızca DB Migrate'tedir:
|
||
`WizardDataSeeder` ekrana özgü kayıtları (veri kaynağı, ListForm, alan, iş akışı) kurar,
|
||
`ListFormSeedDataApplier` satırları ekler. Deploy/import dosyaya `Pending` yazar; Wizard Manager
|
||
onu "Migrate bekliyor" gösterir (`WizardFileInfoDto.IsPending`), migrate önceki sürümün kayıtlarını
|
||
(`Pending.Replaced`) kaldırıp ekranı yeniden kurar ve `Pending`'i siler. Yetkiler **hiçbir role
|
||
otomatik atanmaz** (admin dahil); rol ekranından verilir. Deploy'a veritabanı yazan ya da silen bir
|
||
adım ekleme. Ayrıntı: `lowcode.instructions.md` §4.0.
|
||
- **MCP ile demo veri** → geliştirme isteği örnek satır taşıyorsa (prompt'taki tablo/liste, ekran
|
||
görüntüsü) ekran `deploy_wizard`'a `SeedData: true` ile gider (`SeedFilePath = data/{ListFormCode}.json`
|
||
; `SeedSync*` bayrakları **kapalı** kalır — yalnızca istek ekrandan girilen kayıtların dosyaya
|
||
yansımasını açıkça isterse `SeedSync: true`, wizard ekranında da aynı; `ListFormWizardDto.BuildListForm`) ve satırlar aynı çağrıda `seedRows` ile
|
||
`configs/seeds/data/{ListFormCode}.json` dosyasına yazılır (`McpDevelopAppService`; alan adları
|
||
ekrana göre doğrulanır, `Guid` anahtarı sunucu üretir). Migrate ekranı kurar, ardından satırları
|
||
ekler; MCP migrate'i beklemez. Talep döngüsü `Testing`'de biter, `Staging`'e kullanıcı alır. Dosya elle yazılmaz, görselden okunamayan değer uydurulmaz.
|
||
`SeedData` wizard ekranında sorulmaz ama düzenlemede korunur (`RefreshFrom` + UI modeli).
|
||
Ayrıntı: `lowcode.instructions.md` §0.10.1.
|
||
- **Talepteki görsel** → `Sas.DevRequests` açıklamasına yapıştırılan görsel base64 `data:` URI olarak
|
||
kalır, hiçbir yere yüklenmez. `start_dev_request` / `get_dev_request` onu metinden ayıklar
|
||
(`McpDevRequestImages`) ve MCP resim içeriği (`ImageContentBlock`) olarak döndürür; metinde
|
||
`[image-N]` kalır. Sonucun tamamı istemcinin araç sonucu sınırına (Claude Desktop 1 MB) sığar:
|
||
kalan pay görsellere bölünür, payını aşan görsel `IMcpImageShrinker` (Host'ta SkiaSharp) ile küçültülüp
|
||
JPEG'e çevrilir, yine sığmayan gönderilmez ve metinde bildirilir; kaynak açıklama değişmez.
|
||
Açıklama bu yüzden `update_row` ile yeniden yazılmaz. `SubTasks` de
|
||
`add_dev_request_subtasks` ile yazılır: atanan kişi bağlanan kullanıcı, bitiş tarihi bugün —
|
||
sunucu doldurur, modele bırakılmaz. `Timesheets`'i tamamen sunucu yazar
|
||
(`IMcpDevRequestTimesheetRecorder`): `get_dev_request`/`start_dev_request` talebi etkin talep yapar,
|
||
sonraki her üretim/veri yazma işlemi (`create_table`, `save_sql_object`, `generate_crud_endpoints`,
|
||
`deploy_wizard`, `delete_wizard`,
|
||
`save_component`, `delete_component`, `save_report_category`, `save_report_template`,
|
||
`delete_report_template`, satır araçları, `save_dev_request_analysis`,
|
||
`append_dev_request_note`, `add_dev_request_subtasks`) ona `Sas.Mcp.Timesheet.*` anahtarından Türkçe
|
||
açıklama ve ölçülen süreyle bir satır ekler; yalnızca talebi açan `start_dev_request` satır yazmaz. Yeni bir üretim/veri yazma aracı eklenirse kaydedici çağrısı ve dil anahtarı da eklenir.
|
||
Satır aracın UoW'si commit edildikten sonra ayrı UoW'de yazılır; aynı UoW'de yazmak Dapper
|
||
transaction'ının `Sas_H_DevRequest` kilidini EF'in ayrı bağlantısından bekletip zaman aşımına düşürür.
|
||
- **Talep analizi** → `Description` talebin yazıldığı hâliyle kalır; MCP ona yazmaz. Analiz ve
|
||
üretim notları `Analysis` kolonundadır (Todo panosunda `AnalysisExpr`, Açıklama yanında sekme).
|
||
Durum sırası `Backlog → Analysis → Ready to Start → …`: "Analiz et" `save_dev_request_analysis`
|
||
ile yalnızca üretimi başlamamış (`Backlog`/`Analysis`/`Ready to Start`) talebi analiz eder (ekran/tablo üretmez,
|
||
modül dokümanına yazmaz — dosya ve dal bölümü üretimde, deploy ile açılır; onaylanan analiz bölümün
|
||
"2. Analiz ve kararlar" alt bölümüne üretimde `save_module_doc` ile yazılır). Durumu sunucu
|
||
`productionReady` ile belirler: "Üretime hazır: Evet" → `Ready to Start`, şartlı ya da Hayır →
|
||
`Analysis` (kullanıcı kararı bekler). Üretim notu `append_dev_request_note` ile `Analysis`'e eklenir.
|
||
Analiz standardı tek yerdedir: `lowcode.instructions.md` §0.12; prompt'lar oraya yönlendirir.
|
||
- **MCP şema araçları** → tablo/sütun bilgisi canlı katalogdan değil `Sas_H_DbTableColumn`
|
||
sözlüğünden okunur (`IMcpSchemaDictionary`). Sözlüğün alan adları
|
||
`dbo.Sas_H_ListFormFieldsDictionaryJson` prosedürünün ürettiği JSON ile birebir aynıdır;
|
||
prosedür değişirse `McpDictionaryListForm`/`McpDictionaryField` de değişir. Sözlük tablo kataloğunu
|
||
değil ekranların alanlarını döker: bir tablonun ekranda alan olarak tanımlanmamış kolonunu MCP göremez.
|
||
Modelin okuması gereken kolon (ör. `AbpUsers`'ın kişisel bilgileri) ilgili ekranda gizli alan olarak durur.
|
||
- **MCP OAuth** → bilinen sağlayıcılar (Claude, ChatGPT) seed edilmiş `Platform_Mcp` istemcisini,
|
||
yerel `mcp-remote` proxy'si açık onaylı `Platform_Mcp_Local` istemcisini,
|
||
diğer MCP istemcileri `/connect/register` ile üretilen `mcp_` önekli açık onaylı istemcileri
|
||
kullanır (`McpOAuthClientManager`; seed `McpOAuthClientDataSeedContributor`). Bilinen sağlayıcı
|
||
eklemek = DbMigrator'daki `Platform_Mcp:RedirectUris` listesine callback eklemek. MCP
|
||
istemcisi kararı tek yerdedir (`McpConsts.IsMcpClient`): `resource` parametresi yok sayılır ve
|
||
token kullanıcı oturumuna bağlanmaz; token ucuna yeni bir oturum yolu eklenirse bu istisna
|
||
korunur, yoksa connector web oturumunu düşürür.
|
||
- **MCP yalnızca seed dosyası yazar** → Wizard gibi: geliştirme araçları (`create_table`,
|
||
`save_sql_object`, `generate_crud_endpoints`, `deploy_wizard`) yalnızca `configs/seeds/` altına dosya
|
||
yazar, veritabanına DB Migrate yansıtır. Yeni bir geliştirme aracı veritabanına yazan AppService'i
|
||
çağırmaz, seed dosyasını üreten ortak metodu kullanır (ör. `CrudEndpointSeedFileDto.Generate/Write`;
|
||
diyaloğun servisi de aynı metottan geçer). Hata kodu yalnızca migrate'i bozacak girdi içindir;
|
||
düzeltilebilen girdi (bilinmeyen seçenek, boş/tekrar eden index) sessizce normalize edilir.
|
||
Kolon adları tahmin edilmez: şema araçları geliştiriciye migrate bekleyen wizard ekranlarını ve
|
||
seed-only tabloları da döner (`McpSchemaAppService.GetSeedOnlySourcesAsync`). `deploy_wizard`
|
||
girdisi dosya biçimi (`{"Wizard": {...}}`, başlıklı) ya da düz cevaptır (`WizardSeedFileDto.FromDeployJson`);
|
||
bloklar isteğe bağlıdır (yazılmayan blok `ApplyDefaults` ile dolar), kısayol cevap bloktan üstündür,
|
||
eski biçimin `ListForm`/`Fields` bölümleri reddedilir (`Sas.ListForm.WizardError.LegacySectionsNotAccepted`);
|
||
`get_wizard` yeni biçimi döner.
|
||
- **MCP yetki vermez** → yeni bir MCP aracı kullanıcının uygulamadaki yetkisini aşamaz: liste verisi
|
||
ekranın sorgusundan (`IListFormSelectAppService`/`IListFormDataAppService`) geçer; liste
|
||
yetkisini aşan bir yetenek (ham SQL gibi) `McpToolNames.RequiredPermissions`'a uygulamadaki
|
||
karşılık gelen yetkiyle eklenir. `create_table` bu yüzden `.Write`'a ek olarak `Sas.SqlQueryManager`
|
||
ister; tablo scriptini tasarımcının `generateCreateTableSql` çıktısıyla aynı biçimde üretir
|
||
(`McpTableCreator`) ve **veritabanında çalıştırmaz** — MCP veritabanı kurmaz, tabloyu da ekran gibi
|
||
kullanıcının çalıştırdığı DB Migrate kurar; wizard deploy'u kurulmamış tablonun kolonlarını seed
|
||
scriptinden okur (`PostgreSqlTableScript.ReadSeedColumns`) — tasarımcının şablon kolonları ya da script biçimi değişirse o da değişir. Tasarımcı
|
||
(`SaveTableScriptAsync`) ve `create_table` T-SQL dosyanın PostgreSQL eşini `PostgreSqlTableScript` ile
|
||
üretip `postgres/object/` altına yazar; script biçimine yeni bir deyim eklenirse çevirisi de eklenir,
|
||
yoksa PG eşi yazılmaz (log uyarısı). Düşürülen nesnenin dosyası iki klasörden de silinir.
|
||
- **Çalışan (persona) ekranları ve profil** → çalışanın/yöneticinin kendi listeleri İK menüsüne
|
||
konmaz: menüsüz `Hr.My*`/`Team*` wizard + Intranet panosu widget'ı. Profil sayfasının İzinlerim /
|
||
Puantajım / Performansım sekmeleri persona ekranlarını salt okunur gösteren çekirdek bileşenlerdir
|
||
(düzenle/sil yok, tek aksiyon izin talebi oluşturma); yeni persona
|
||
ekranını profile eklemek kod + rota değişikliğidir (onay). Çalışan Bilgileri sekmesi ve Genel sekmesi
|
||
kullanıcının kendi alanlarını `my-profile` ek alanlarından okur (`PlatformProfileAppService.ProfileExtraProperties`);
|
||
sağlık, ücret ve işten çıkış alanları bu listeye girmez. Ayrıntı: `lowcode.instructions.md` §0.12.1.
|
||
- **Bildirim kapsamı** → bildirim tipi, kuralı ve kuyruğu kiracıya aittir (`Adm_T_NotificationTemplate`,
|
||
`Adm_T_NotificationType`, `Adm_T_NotificationRule`, `Adm_T_Notification`; dördü de `IMultiTenant`) ve ekranların tamamı
|
||
Administration › Bildirimler altındadır. Ayrı bir manuel gönderim tablosu yoktur: **Bildirimler**
|
||
ekranının ekleme formu elle gönderimdir, uç alıcı × kanal açılımını kuyruğa yazar
|
||
(`list-form-dynamic-api/notification-insert`). Yeni kiracıya varsayılan tür/kural kümesi
|
||
gelir: kendi veritabanı olan kiracı seed dosyasından alır, host veritabanını paylaşana
|
||
`NotificationTenantDefaultsSeedContributor` aynı dosyalardan yeni kimliklerle kopyalar; iki yol
|
||
aynı kiracıda çalışmaz. Kiracı verisine dokunan zamanlanmış işin
|
||
`Scope` alanı `Tenant` ya da `Host ve Kiracılar` olmalıdır — `Host` kapsamında çalışan bir iş
|
||
kiracı satırlarını göremez (`WorkerScopeEnum`, `PlatformBackgroundWorker`).
|
||
- **Bildirim bayrakları** → sıra değişmez: `IsSent` (iletildi) olmadan `IsRead` yazılmaz, `IsRead`
|
||
ile `ReadTime` birlikte yazılır. `UiActivity` kayıt oluşurken iletilmiş sayılır, `UiToast`/`Desktop`
|
||
tarayıcıda gösterildiğinde (`NotificationChannels.BrowserChannels`; `sent-many` ucu yalnızca bu
|
||
kümeyi işaretler), kalan kanalları `NotificationWorker` işaretler — gönderim okuma değildir.
|
||
Kullanıcı tercihi (`IsCustomized` + `RecipientType = User`) kural değildir, yalnızca sahibini
|
||
kapatır; alıcıyı kurallar belirler. Yeni bildirim üretildiği anda `/notificationhub` ile
|
||
duyurulur (`INotificationRealtimeNotifier`; sözleşme bildirim modülünün Domain'inde, SignalR
|
||
gerçeklemesi barındıran uygulamada — modül katmanı ASP.NET Core'a bağlanmaz) — duyuru en iyi çabadır, istemcideki yedek yoklama kaldırılmaz.
|
||
Ayrıntı: `README.md` §13.3–§13.5.
|
||
- **SEO** → **ayrı bir SEO tablosu yoktur**; meta, yolu tanımlayan kaydın üzerinde yaşar. Üç
|
||
parça birlikte çalışır: genel varsayılanlar `Adm.SiteManagement.Seo.*` ayarları, yol bazlı
|
||
alanlar `Route.Seo*` ve `CustomComponent.Seo*` (ikisinin de `SeedSync*` bayrakları açık),
|
||
istemci tarafı `SeoHead`. Üçü de uygulama konfigürasyonunun `seo` alanından beslenir
|
||
(`PlatformApplicationConfigurationContributor` → `SeoAppService.GetConfigAsync`); `sitemap.xml`
|
||
ve `robots.txt` aynı servisten `SeoController` ile yayınlanır. Aynı yolu iki kaynak da
|
||
tanımlıyorsa bileşen kazanır — çalışma zamanında da bileşen fiziksel rotayı gölgeler.
|
||
**Ziyaretçiye açık yeni bir yol üreten her iş SEO alanlarını da doldurur** (standing default 7).
|
||
Sitemap'e giren kaynaklar (`Pub` menüleri, aktif custom component rotaları, yayınlanmış blog
|
||
yazıları) `SeoAppService` içindedir; yeni bir public kaynak türü eklenirse toplayıcıya da
|
||
eklenir, yoksa sayfa sitemap'te görünmez. Yeni bir meta alanı = iki entity + `SeoRouteDto` +
|
||
`SeoFields` (seeder) + `SeoHead` birlikte.
|
||
- **Yeni bir dil anahtarı** → `configs/seeds/data/Sas.Languages.LanguageText.json` (EN + TR iki satır;
|
||
`Id` = `Sas_H_LanguageText:{ResourceName}:{Key}:{CultureName}` adından URL isim alanlı v5 GUID); eklemeden önce anahtarın zaten var olup
|
||
olmadığını kontrol et.
|
||
- **Custom component sınıfları** → bileşen kodu veritabanında yaşar, Tailwind derlemede yalnızca
|
||
seed dosyasını görür. Seed dosyası `ui/` dışında olduğu için aynı sınıflar `ui/safelist.txt`
|
||
dosyasına da yazılır (`ui/twSafelistGenerator.ts` Vite eklentisi, derlemede otomatik); yoksa
|
||
uygulama sunucuda stilsiz kalır. İkinci bir safelist dosyası açma. Çalışma anında veriden üretilen
|
||
sınıf (`bg-${renk}-500`) dosyaya değil `src/assets/styles/tailwind/index.css`'teki `@source inline`
|
||
satırına eklenir. Sınıf adları Tailwind 4'tür (`shadow-xs`, `rounded-sm`, `bg-black/50`,
|
||
`shrink-0`, `x!`); tema `tailwind.config.ts`'te değil aynı dosyanın `@theme` bloğundadır.
|
||
- **Custom component kaydı/silinmesi** → `data/Sas.DeveloperKit.CustomComponents/{Id}.json`
|
||
(Custom Components ekranının seed verisi); ekran klasör modundadır, her bileşen kendi satır
|
||
dosyasındadır. Hem listeden hem Visual Designer'dan (`CustomComponentAppService`) yapılan kayıt
|
||
aynı klasöre yansır; kullandığı endpoint'ler `crud/{Entity}.json`.
|
||
- **Wizard dosyası** → tek başına taşınmaz; export zip'i **özyinelemeli bağımlılık kapanışını**
|
||
taşır (`WizardExportCollector`): alt formların wizard dosyaları, ekranın `SeedFilePath` veri
|
||
aynası, lookup sorgularının işaret ettiği nesnelerin scriptleri, `crud/` ve
|
||
`{sql|postgres}/{object|execute}/` dosyaları. `Custom` yolunda `data/` girdisi yalnızca ilgili
|
||
bileşen ve bağımlılık zincirine süzülür; import'ta hedef dosyanın üstüne yazılmaz, satırlar
|
||
anahtar üzerinden birleştirilir. Kapanışa yeni bir bağımlılık türü eklenirse
|
||
`WizardImportValidator`'daki karşılığı da eklenir — biri toplar, diğeri eksikliğini yakalar.
|
||
- **Talep bağı (`DevRequest`)** → MCP `deploy_wizard` wizard dosyasına etkin talebi
|
||
(`IMcpDevRequestTimesheetRecorder.GetActiveRequestIdAsync`) `DevRequest { Id, Title }` olarak
|
||
yazar; bağ modelin girdisinden gelmez (`ListFormWizardDto.DevRequestId` `[JsonIgnore]`). Elle
|
||
yapılan kayıt önceki bağı korur, talepsiz wizard'da bölüm hiç yazılmaz. Wizard Manager talebi
|
||
gösterir, kaydına bağlar ve talebin bütün wizard'larını tek kapanışta export eder
|
||
(`export-dev-request`; `WizardExportCollector.CollectAsync` birden çok dosya alır). Wizard
|
||
dosyasını yeniden yazan yeni bir yol eklenirse `DevRequest` korunur — dosyayı DTO olarak okuyup
|
||
yazan yollar bunu kendiliğinden yapar.
|
||
- **Şema değişikliği** → ayrım **kayıp riskine** göredir. Eksik kolon **eklenir** (kayıpsız;
|
||
`ALTER TABLE ADD`), var olan kolon **hiçbir zaman değiştirilmez**, hiçbir kolon düşürülmez,
|
||
hiçbir tablo `DROP` edilmez. Ölçü farkında (uzunluk/kesinlik/nullability) import durdurulur ve
|
||
fark somut olarak bildirilir; varsayılanı olmayan `NOT NULL` bir kolonun dolu tabloya eklenmesi
|
||
de durdurulur — hangi değerin yazılacağı iş kararıdır. Karşılaştırma tek yerdedir
|
||
(`SqlTableSchemaComparer`), analiz ve kapanış aynı sonucu kullanır.
|
||
Karşılaştırmada tip **adı** kullanılmaz — sağlayıcı katalogları farklı kanonik ad raporlar ve
|
||
metin karşılaştırması sahte fark üretir. Genel kural: **sağlayıcıya göre değişen bir değeri
|
||
metin olarak karşılaştırma, katalogdan dinamik oku.**
|
||
- **Veritabanına dokunan her import ucu** → `EnsureImportNotBlocked` ile başlar. Analizdeki
|
||
engelleme kararı sunucuda `blocked` dosyasında durur; yeni bir uç eklenirse kontrolü de eklenir,
|
||
yoksa karar istemci tarafından atlanabilir hâle gelir.
|
||
- **Wizard import'u** → analiz dosyalar yazılmadan **önce** doğrular (`WizardImportValidator`);
|
||
`Error` seviyesinde bir bulgu varsa oturuma `blocked` işareti düşer ve `ApplyImport` reddeder.
|
||
Kapanış (`CompleteImport`) bağımlılık sırasıyla çalışır: `sql/object` → `sql/execute` → `crud`
|
||
→ `data` → `wizard`. Tablo eksikse önce scripti çalışır; sıra değiştirilmez. Ekran import'ta
|
||
**kurulmaz**: wizard dosyasına `Pending` yazılır ve ekran deploy'daki gibi DB Migrate'te kurulur;
|
||
dosyanın `Wizard` bölümündeki bloklar olduğu gibi uygulanır (`BuildListForm/BuildFields`), varsayılanlar
|
||
yeniden üretilmez — tasarımcı değişiklikleri bloklarda durduğu için korunur. Wizard'ın menü/yetki/dil satırlarını
|
||
taşıyan platform dosyaları `data` adımında uygulanmaz (`WizardSeedRowStore.ListFormCodes`).
|
||
Engelleyici bulgu iki sınıftır: **arşiv kaynaklı** (`RequiresReexport` — bozuk/eksik dosya,
|
||
karşılığı olmayan referans) hedef ortamda giderilemez, export tekrarlanır; **ortam kaynaklı**
|
||
(eksik tablo, şema farkı) ekrandan giderilir. Yeni bir `Error` eklerken hangisi olduğu işaretlenir.
|
||
Engelleme **oturum genelidir, dosya bazlı değildir** —
|
||
import ya bütün olarak çalışır ya da hiç. Bunun bedeli, her engelin ekran içinde çözülebilir
|
||
olmasıdır: tabloya dair bir bulgu üretirken `TableName` (+ tablo hedefte yoksa `TableMissing`)
|
||
doldurulur, istemci tablo tasarımcısını buna göre açar.
|
||
- **`ListForm.SeedFilePath` dolu bir ekran** → `SeedSyncInsert/SeedSyncUpdate/SeedSyncDelete`
|
||
bayraklarından işaretli olan işlemler seed klasöründeki hedefe yansır
|
||
(`ListFormSeedDataSynchronizer`); yalnızca host bağlamındaki kayıt yansır, kiracının kaydı
|
||
veritabanında kalır. Hedef iki biçimdedir ve ayrım uzantıdadır: `.json` ile biten
|
||
değer **tek dosyadır** (`data/{ListFormCode}.json`, bütün satırlar orada), uzantısız değer
|
||
**klasördür** (`data/{ListFormCode}/{anahtar}.json`, her satır kendi dosyasında) ve 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
|
||
(varsayılan tek dosya). **Biçimin tek kaynağı ekranın tanımıdır**; diskte ne bulunduğu kararı
|
||
değiştirmez ve tanım kod içinde ekran adıyla sabitlenmez. Hedefi okuyan/yazan her yol
|
||
(senkronizasyon, export/import, temizleme, wizard satır deposu, MCP demo satırları, bildirim
|
||
varsayılanları) `ListFormSeedTarget` üzerinden gider; paylaşılan ekranların tanımı
|
||
`ListFormWizardAppService.ReadSeedFilePathsAsync` ile okunup bileşenlere verilir. Applier `data/`
|
||
altını özyinelemeli tarar ve **bir klasörün tamamını tek unit of work'te** uygular — ekran
|
||
tanımı, alan listesi ve veri kaynağı satır dosyası başına değil ekran başına bir kez çözülür;
|
||
dosyadaki `ListFormCode` boşsa ekran kodu klasör adından gelir. Ekran bir hedef tanımlıyorsa
|
||
yalnızca o hedef uygulanır; tanımın dışında kalan dosya (biçimi değişmiş ekranın eski dosyası)
|
||
uyarıyla atlanır. Biçimi değiştirmek tanımı (ListForm detayı ya da seeder) değiştirmektir;
|
||
yeniden wizard deploy'u tanımlı hedefi korur.
|
||
Migrate/seed sırasında ve wizard import'unun kapanışında
|
||
(`CompleteImport`) `ListFormSeedDataApplier` dosyayı geri uygular. Migrate/seed'de kayıt yoksa
|
||
eklenir, varsa dokunulmaz; import'ta `overwriteExisting` açıktır: var olan kayıt dosyadaki
|
||
değerlerle güncellenir ve soft delete izleri (`IsDeleted`/`DeletionTime`/`DeleterId`) temizlenir. Satırı veritabanından okuyan senkronizasyon açık UoW varsa commit sonrasında kendi UoW'sinde çalışır (EF ile yazan çağıran, ör. `MenuAppService`, aksi hâlde eski değeri dosyaya yazardı); yalnızca dosyaya dokunan `RemoveRowsByFieldAsync` hemen çalışır. Yeni bir kayıt yolu (yeni endpoint, toplu işlem, tipli AppService) eklenirse
|
||
senkronizasyon çağrısı da eklenir; yoksa dosya bayatlar.
|
||
- **Seed kapsamı** → `configs/seeds/` tek klasördür; `host/` ya da `tenants/{tenantId}/` yoktur.
|
||
Host ve kendi veritabanı olan her tenant aynı dosyalarla kurulur, host veritabanını paylaşan
|
||
tenant'ta dosyalar yeniden uygulanmaz (`SeedFileScope`; migrate kararı `DataSeedContext`'e yazar).
|
||
Dosyayı okuyan yeni bir seeder `SeedFileScope.IsEnabled` ile başlar. Dosyalar yalnızca host
|
||
bağlamında yazılır; dosyaya yazan yeni bir yol tenant bağlamında yazmaz. Bekleyen wizard kurulumu
|
||
(`Pending`) dosyaları uygulayan son veritabanında kapanır (`SeedFileScope.CompletesPending`).
|
||
Tek istisna `{sql|postgres}/host/{object,execute}`: yalnızca host'ta çalışır; tenant veritabanında
|
||
karşılığı olmayan nesneye (HangFire tabloları) dayanan script buraya konur, tablo önekine bakılmaz.
|
||
Kendi veritabanı olan tenant'ta EF ile yalnızca `IMultiTenant` varlıklar o veritabanına gider;
|
||
ListForm, menü ve yetki tanımını ABP her zaman host'ta okur. Kiracı tablosunun satırları kiracı
|
||
adıyla kayıtlı `DataSource` üzerinden gider. Kiracı yalnızca kimliğiyle değiştirildiğinde ad boş
|
||
kalır; bu durumda adı `DataSourceManager` kiracı kaydından okur. Yeni tenant yöneticisi ilk
|
||
seed'de kiracı tarafı bütün yetkileri alır (`PlatformIdentityDataSeeder`). Bunun nedeni,
|
||
DbMigrator'da dinamik yetki deposunun kapalı olmasıdır: ABP'nin yetki seed'i seed dosyasından
|
||
gelen yetkileri göremez.
|
||
- **Seed dosyası yazmak** → veri dosyası `ListFormSeedDataFileDto.Serialize`, wizard dosyası
|
||
`WizardSeedFileDto.ToSeedJson` ile yazılır; düz `JsonSerializer.Serialize` + `File.WriteAllText`
|
||
kullanılmaz. System.Text.Json emojiyi her zaman vekil çift kaçışına çevirir; dosya baştan
|
||
yazıldığında bütün emojiler sahte fark üretir. İkisi de `SeedJsonText.RestoreSurrogatePairs`'ten
|
||
geçer. Veri dosyasının `GeneratedAt` değeri oluşturma anıdır: `Serialize` yalnızca boşsa doldurur,
|
||
hiçbir yazma yolu onu güncellemez — ekleyip geri kaldıran işlem (wizard deploy + silme) dosyayı
|
||
birebir önceki hâline döndürür. `crud/` dosyalarının damgası ayrıdır ve karar verir (aşağıda).
|
||
- **`crud/{Entity}.json`** → uygulama kuralı `CrudEndpointSeedApplier`'dadır ve damga
|
||
karşılaştırmalıdır: eksik endpoint eklenir, mevcut kayıt yalnızca dosyanın `GeneratedAt` değeri
|
||
kaydın `LastModificationTime`/`CreationTime` değerinden yeniyse güncellenir/silinir. Migrate
|
||
kullanıcının runtime revizyonunu ezmez. Yeni bir seeder yazarken bu kuralı taklit et: **migrate
|
||
varsayılanı eklemektir, ezmek değil.**
|
||
- **Modül = menü = tablo öneki = yetki kökü** → platformun C# seeder'lı kök menüleri `Sas`,
|
||
`Adm`, `Hr` (İK) ve `Pub` (kök menü `Pub.Header`) modüllerindedir
|
||
(`TableNameResolver.MenuPrefix` yalnızca bunları bilir); wizard ile üretilen modüller kendi kök menüsü ve
|
||
tablo önekiyle gelir — `Prj`, `Prc`, `Stk` (`Stk_`),
|
||
`Mnt` (`Mnt_`), `Prd` (`Prd_`); tam liste `data/Sas.Menus.Menu.json`
|
||
kök satırlarıdır. Bir ekranı modüller arası
|
||
taşımak birlikte yapılır: `TableNameResolver` (`MenuPrefix`) + `AppCodes` (Seed/Platform) +
|
||
`ListFormSeeder_{Sas,Adm,Hr}.cs` + `data/{ListFormCode}.json` adı + menü/yetki/dil
|
||
satırları + route `Authority`. Yetki `Modules` değeri: Sas/Adm `Sas|Adm`, İK `Sas|Hr`
|
||
(`Sas` host, `Adm`/`Hr` kiracı görünürlüğü). İK departman/pozisyon `BranchId` zorunludur; seed
|
||
satırı host şubesine (`PlatformConsts.Branches.BranchIdDefaultValue`) bağlanır.
|
||
- **Menü ağacı ve yetkiler** → `configs/seeds/data/Sas.Menus.{Routes,Module,Menu}.json` ve
|
||
`Abp.Identity.Permission{Groups,s}.json`; platformun kendi menü/yetki kayıtları da diğer liste
|
||
verisiyle aynı mekanizmadan (`ListFormSeedDataApplier`) geçer, ayrı bir seeder yoktur. `Order`
|
||
alanı bağımlılık sırasını kurar: route → modül → menü, yetki grubu → yetki, ülke grubu → ülke →
|
||
şehir → ilçe (`Sas.Definitions.{CountryGroup,Country,City,District}.json`), departman → onaycı (`Hr.Department` → `Adm.Definitions.Approver`), bildirim tipi → kuralı
|
||
(`Adm.Notifications.Notification{Types,Rules}.json`). Para birimi, hitap, AI bot
|
||
(`Sas.Definitions.{Currency,ContactTitle,AiBot}.json`), ayar tanımları (`Sas.SettingDefinitions.json`)
|
||
ve zamanlanmış işler (`Sas.BackgroundWorkers.RecurringJobs.json`), dil → metin
|
||
(`Sas.Languages.{Language,LanguageText}.json`; `LanguagesData.json` + `LanguageDataSeeder` kaldırıldı) da buradadır; `HostData.json`
|
||
kaldırıldı, `HostDataSeeder` yalnızca bağlantı cümlesi appsettings'ten gelen varsayılan veri kaynağını kurar. Dosyalar ekranın
|
||
anahtarını (`KeyFieldName`) kullanır; `Guid` anahtarlı satırın `Id`'si iş anahtarından türetilmiş
|
||
sabit bir GUID'dir, satır eklerken aynı kuralla üret. `Sas.Menus.Routes.json` dosyasına
|
||
yalnızca var olan satırın `Seo*` alanları için dokunulur (standing default 7); rota eklemek,
|
||
silmek ya da yolunu değiştirmek çerçeve dışıdır.
|
||
- **Public site** → menü `Menu` tablosunda `ModuleId = "Pub"` altında (`PlatformConsts.PublicSite`);
|
||
`Pub.Header` hem üst menüyü hem alt bilgideki hızlı bağlantıları besler (ikisi de
|
||
`PublicNav` düğümü) ve ziyaretçiye uygulama konfigürasyonunun `publicMenus` alanıyla taşınır
|
||
(menü ucu `[Authorize]`). Sayfa ise Custom Component'tir: `/admin/` ile
|
||
başlamayan `RoutePath` otomatik public route olur ve aynı yolu paylaşan fiziksel route'u gölgeler;
|
||
`RoutePath` boş bırakılabilir (rotasız bileşen). Yeni public sayfa =
|
||
`data/Sas.DeveloperKit.CustomComponents/{Id}.json` satırı + `Pub` menü kaydı; ikisi birlikte yazılır.
|
||
Üst ve alt bilgi rotasız bileşenlerdir (`PublicHeader`, `PublicFooter`), layout ikisini de
|
||
`renderComponent` ile çizer; rotasız bileşende `RoutePath` **NULL** yazılır (tekil indeks yalnızca
|
||
dolu değerleri kapsar). Üst bilginin davranışı `components/publicSite` katalog düğümlerinde,
|
||
yerleşimi tasarımcıdadır.
|
||
**Public bileşenlerin hepsi tasarımcı belgesidir** (`Props.visualDesigner`, `sourceMode: visual`);
|
||
kod belgeden üretilir (`generateDesignerCode`), elle yazılmış koda dönülmez. Runtime kapsamında
|
||
`react-icons`/`react-router` yoktur — ikon `PlatformIcon` düğümü, gezinme `a` düğümü. Kanvasın
|
||
doğrudan çizdiği katalog düğümleri tek sözlüktedir (`componentEditor/runtimeNodes.ts`); yeni bir
|
||
düğüm eklerken bileşen + bu sözlük + `catalog.ts` tanımı + dil anahtarı birlikte yazılır. Elle
|
||
yazılmış bir bileşen tasarımcıda kod modunda açılır, kanvasa çevrilmez. Ayrıntı: `README.md` §10.4.
|
||
- **Çok değerli alan (`dxTagBox` / çoklu `dxGridBox`)** → tek metin kolonunda `|` ile saklanır.
|
||
Ekran varsayılan `list-form-data/*` ucundan kaydediyorsa birleştirmeyi `QueryHelper` yapar;
|
||
tipli DTO alan bir uca (`list-form-dynamic-api/...`, Custom Endpoint, Dynamic Service)
|
||
bağlıysa DTO alanına `[JsonConverter(typeof(MultiValueStringJsonConverter))]` eklenir —
|
||
yoksa istek `400` ile düşer. Ayrıntı: `lowcode.instructions.md` §4.3.2.1.
|
||
|
||
### Definition of done
|
||
|
||
- Ölü kod bırakma: kullanılmayan `using`, private üye, alan, DTO, hook, servis, tip. Bir tip
|
||
silinmeden önce **repo geneli** (api + ui + configs) referans taraması yapılır; DI ile çözülen
|
||
tipler düz metin aramasında referanssız görünür.
|
||
- Yorum satırına alınmış kod bloğu bırakılmaz.
|
||
- Seed tarafında ölü artefakt yok (`lowcode.instructions.md` §8.6): yerini yenisine bırakan SQL nesnesi,
|
||
wizard ve veri dosyası silinmiş; her `sql/` dosyasının `postgres/` eşi var; kullanılmayan dil anahtarı
|
||
wizard'ın `Language.Texts`/`InsertedRecords`'undan ve dil dosyasından çıkarılmış.
|
||
- Modül/ekran/bileşen ekleyen ya da davranışını değiştiren iş modül dokümanını
|
||
(`configs/seeds/docs/modules/{Menü}.md`) yazar veya günceller; liste ekranının açıklaması
|
||
ayrıntılıdır (standing default 14–15).
|
||
- `api/` değişikliğinden sonra: `dotnet build -p:EnforceCodeStyleInBuild=true` — 0 hata, yeni
|
||
`IDE00xx` yok.
|
||
- `ui/` değişikliğinden sonra: `npm run typecheck` ve `npm run lint`.
|
||
- Yorumlar **neden**i anlatır, ne yaptığını değil. Kod zaten ne yaptığını söylüyor.
|
||
- **Dokümantasyon kodla birlikte güncellenir.** Platformun davranışını değiştiren her değişiklik
|
||
(yeni/kaldırılan seeder, seed klasörü ya da dosya biçimi, ListForm sözleşmesine alan ekleme,
|
||
yeni yetki/dil anahtarı, katman sınırı, yeni mekanizma) aynı commit içinde ilgili `.md`
|
||
dosyasına yansıtılır: `README.md` (ne nerede yaşıyor, klasör düzeni, tablolar),
|
||
`.github/instructions/lowcode*.instructions.md` (artefakt/yetenek sözleşmesi),
|
||
`dotnet.instructions.md` (kod standardı, katman kuralı), `CLAUDE.md` (çalışma kuralı).
|
||
Değişiklik hangi md'de karşılığı olduğu bilinmiyorsa önce grep ile eski anlatım aranır ve
|
||
düzeltilir; bayat kalan bir satır bırakma. Doküman güncellemesi "sonraya" bırakılmaz.
|
||
|
||
### Tone
|
||
|
||
- Türkçe soruya Türkçe cevap.
|
||
- Platformu tanıtma, mimariyi özetleme, ne yapacağını anlatıp sonra bir de ne yaptığını özetleme.
|
||
- Cevabın boyutu sorunun boyutu kadar olsun.
|