From 677a62bd79d5c4188bfbde0e79444247a635fff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?= <76204082+iamsedatozturk@users.noreply.github.com> Date: Tue, 29 Sep 2026 09:14:07 +0300 Subject: [PATCH] =?UTF-8?q?Md=20dosyalar=C4=B1=20d=C3=BCzenleme=20ve=20?= =?UTF-8?q?=C3=B6l=C3=BC=20kodlar=20kald=C4=B1r=C4=B1ld=C4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/instructions/ai.instructions.md | 10 +- .github/instructions/dotnet.instructions.md | 2 +- .github/instructions/lowcode.instructions.md | 36 +++++--- README.md | 58 +++++++----- .../McpDevelopAppService.cs | 2 + .../McpTimesheetTexts.cs | 2 + .../Editors/EditorOptions.cs | 92 ++++++++----------- .../Extensions/AbpPermissionsExtensions.cs | 16 ---- claude.md | 19 ++-- .../host/data/App.Languages.LanguageText.json | 28 ++++++ ui/src/views/shared/htmlEditorOptions.ts | 1 + 11 files changed, 148 insertions(+), 118 deletions(-) diff --git a/.github/instructions/ai.instructions.md b/.github/instructions/ai.instructions.md index 8162f546..331dd63e 100644 --- a/.github/instructions/ai.instructions.md +++ b/.github/instructions/ai.instructions.md @@ -362,8 +362,10 @@ MCP (`/mcp`, `Sozsoft.Mcp` module) — expose ListForm data/queries to an extern (Claude, ChatGPT, or an OAuth-registered MCP client) without new code: configure a server row on the **MCP Servers** screen (`App.DeveloperKit.McpServers`, §12.2 of `lowcode-reference.instructions.md`) with its allowed tools and tenant scope; queries always run -on the calling tenant's own database. Tenant users are read-only over MCP (schema tools, -`query_list`, `run_select`); writing and development tools are host-only. A +on the calling tenant's own database. Tenant users read and write data over MCP but cannot develop +(`McpToolNames.TenantTools`: schema tools, `query_list`, `run_select`, `insert_row`/`update_row`/`delete_row` +— row writes go through the screen's C/U/D contract and need `.Write`); development tools, +`list_queries` and `run_query` are host-only. A request to "let an agent query/update X" is a Decision Flow §1 (ListForm) request first — MCP only gates which of the existing screens/queries an already-authorized client may reach; it never grants access beyond the calling user's own ListForm/query permissions. Ayrıntı: @@ -879,7 +881,7 @@ Default is `List`, so older seed files that lack it keep working. | Kind | Produces | Menu URL | Child permissions | | --- | --- | --- | --- | -| `List` | ListForm + ListFormFields + Route + Menu + Permissions + language keys | `/admin/list/{ListFormCode}` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` | +| `List` | ListForm + ListFormFields + Menu + Permissions + language keys (no Route record; the generic `/admin/list/{code}` route serves it) | `/admin/list/{ListFormCode}` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` | | `Custom` | Menu + Permissions + language keys; the screen is the selected Custom Component | the component's `RoutePath` | `.Create`, `.Update`, `.Delete` only | `Export` / `Import` / `Note` exist only in the ListForm ecosystem. Never propose them on the @@ -891,7 +893,7 @@ embedded elsewhere as a SubGrid/part. Default is `true`. ### 32.4 Permission group selection -The wizard's permission group comes from a select list backed by `GetWizardPermissionGroups`. +The wizard's permission group comes from a select list backed by `ListFormWizardAppService.GetPermissionGroups` (`/api/app/list-form-wizard/permission-groups`). Display names are read from the language key whose name equals the group name. - Selecting an existing group fills `PermissionGroupEn/Tr` (seed file: `Language.PermissionGroupEn/Tr`); diff --git a/.github/instructions/dotnet.instructions.md b/.github/instructions/dotnet.instructions.md index 75a1103b..981f224e 100644 --- a/.github/instructions/dotnet.instructions.md +++ b/.github/instructions/dotnet.instructions.md @@ -31,7 +31,7 @@ Kural: - Nullable, proje bazında ve bilinçli olarak açılır (`enable`). Şu an açık olanlar: `Sozsoft.Platform.Application`, `Sozsoft.Notifications.*`, - test projeleri. + `Sozsoft.Mcp.*`, test projeleri. - **Entity taşıyan projelerde** (`*.Domain`) nullable açılacaksa, aynı PR'da `dotnet ef migrations add` çıktısı gözden geçirilir; şema değişikliği isteniyorsa kabul edilir, istenmiyorsa kolon zorunluluğu `PlatformDbContext` içinde diff --git a/.github/instructions/lowcode.instructions.md b/.github/instructions/lowcode.instructions.md index 5dbd9573..4f720448 100644 --- a/.github/instructions/lowcode.instructions.md +++ b/.github/instructions/lowcode.instructions.md @@ -482,7 +482,8 @@ ya da ziyaretçiye açık yeni bir fiziksel sayfa ürettiğinde. **Genel varsayılanlar kayıtta değil ayardadır:** site adresi, site adı, varsayılan başlık ve açıklama, paylaşım görseli, Twitter hesabı, diller, robots yönergesi, Google doğrulaması, -sosyal hesaplar ve sitemap anahtarı `App.SiteManagement.Seo.*` ayarlarındadır +sosyal hesaplar, varsayılan anahtar kelimeler (`DefaultKeywords`), sitemap anahtarı (`SitemapEnabled`) ve +sitemap dışı yollar (`SitemapExcludePaths`) `App.SiteManagement.Seo.*` ayarlarındadır (`configs/seeds/host/data/App.SettingDefinitions.json`). Bir değer bütün site için geçerliyse oraya yazılır, her yola tekrarlanmaz. Site kökü (`/`) hiçbir kayıt taşımaz; doğrudan bu varsayılanlardan beslenir. @@ -605,7 +606,8 @@ talebin metnini döndürür. Başlık birden fazla talebe uyuyorsa Id ister. `St - `Timesheets` — **sunucu yazar, model hiçbir şey yapmaz.** `get_dev_request` / `start_dev_request` talebi kullanıcının etkin talebi yapar; ardından yapılan her MCP yazma işlemi (`create_table`, `deploy_wizard`, `delete_wizard`, `save_component`, `delete_component`, `insert_row` / - `update_row` / `delete_row`, `save_dev_request_analysis`) o talebe bir satır ekler + `update_row` / `delete_row`, `save_dev_request_analysis`, `append_dev_request_note`, + `add_dev_request_subtasks`) o talebe bir satır ekler (`IMcpDevRequestTimesheetRecorder`). Açıklama `App.Mcp.Timesheet.*` dil anahtarından **Türkçe** üretilir ("Hr_T_Doctor tablosu oluşturuldu"), süre önceki satırdan ya da talebin açıldığı andan bu yana ölçülür (en az 1 dakika, saat olarak), çalışan bağlanan kullanıcı, tarih bugündür. @@ -901,7 +903,7 @@ hiçbir şey silmez**. Tek çıktısı seed dosyalarıdır: | `wizard/{WizardName}.json` | Ekranın `ListForm`/`Fields` sözleşmesi, wizard cevapları, `InsertedRecords`, `Pending` | | `data/App.Menus.Module.json`, `data/App.Menus.Menu.json` | Modül, üst menü, ekranın menüsü | | `data/Abp.Identity.PermissionGroups.json`, `data/Abp.Identity.Permissions.json` | İzin grubu, okuma yetkisi ve alt yetkileri | -| `data/App.Languages.LanguageText.json` | Menü, başlık, açıklama, grup ve alan başlıklarının EN + TR metinleri (`Id` iş anahtarından v5 GUID, `SeedRowIds.LanguageText`) | +| `data/App.Languages.LanguageText.json` | Menü, başlık, açıklama, grup, alan başlıkları ve `Language.Texts` anahtarlarının (widget başlıkları) EN + TR metinleri (`Id` iş anahtarından v5 GUID, `SeedRowIds.LanguageText`) | Satırları `WizardSeedRowStore` yazar; anahtarlar `WizardSeedRowKeys`'ten gelir (export aynı listeyle toplar). **Sahiplik:** bir satır ne veritabanında ne dosyada varsa ya da önceki sürüm ona @@ -942,7 +944,8 @@ sürümünü taşır. "Menu": { "MenuCode": "…", "MenuOrder": 2, "MenuUrl": "…", "MenuIcon": "…", "MenuParentPath": [ { "Code": "…", "Icon": "…", "En": "…", "Tr": "…" } ], // yalnızca derin yolda "MenuParentCode": "…", "MenuParentIcon": "…" }, // "CreateMenu": false ise eklenir - "Language": { "MenuEn": "…", /* Menu/Title/Desc/MenuParent/PermissionGroup · EN + TR */ }, + "Language": { "MenuEn": "…", /* Menu/Title/Desc/MenuParent/PermissionGroup · EN + TR */ + "Texts": [ { "Key": "…", "En": "…", "Tr": "…" } ] }, // Texts yalnızca doluysa "Groups": [ { "Items": [ { "FieldName": "…", "CaptionName": "…", "En": "…", "Tr": "…" } ] } ] // grup Caption/ColCount yok }, @@ -993,7 +996,7 @@ düzenleme izinleri, görünüm option blokları, alt formlar, widget'lar, iş a Dosyada wizard alanları üç başlık altında toplanır — `Menu` (menü kaydı), `Language` (dil metinleri), `Groups` (alan başlıklarının dil karşılıkları); geri kalanı kök seviyededir. -Gruplama ile `ListFormCode → WizardCode` ve `MenuParentModuleId → ModuleId` adlandırmaları +Gruplama ile `ListFormCode → WizardCode`, `MenuParentModuleId → ModuleId` ve `LanguageTexts → Language.Texts` adlandırmaları yalnızca **dosya biçimidir**; C# sözleşmesi (`ListFormWizardDto`) düzdür ve dosya okunurken düzleştirilir (`WizardSeedFileDto.FromSeedJson` ↔ `ToSeedJson`). Aşağıdaki tablolar C# alan adlarını kullanır. @@ -1671,8 +1674,9 @@ Silme ucu (`DeleteFile`) her kaydın akıbetini (`Deleted` / `Kept` + kullananla Düzenleme (`EditFileName`) bu sınıfı çağırmaz; önceki sürümün kayıtlarını migrate kaldırır (§4.0). Wizard'ın yarattığı modül `InsertedRecords.ModuleIds` ile izlenir. -Silme yolunda ekranın veri aynası (`ListForm.SeedFilePath` → `data/{ListFormCode}.json` ya da -klasörü) ekranla birlikte silinir; ekransız uygulanamaz. +Kurulu wizard silinirken ekranın veri aynası (`ListForm.SeedFilePath` → `data/{ListFormCode}.json` ya da +klasörü) ekranla birlikte silinir; ekransız uygulanamaz. Migrate bekleyen wizard'da ayna yalnızca ekran +hiç kurulmamışsa silinir (MCP demo satırları); kurulu ekranın aynası veritabanındaki satırların kaydıdır, kalır. **Ekranın tablosu** wizard'ın ürettiği bir kayıt değildir (tasarımcı ya da `create_table` ile ayrıca kurulur, `InsertedRecords`'ta yoktur) ve **kendiliğinden silinmez**. Silme onayı önce @@ -2124,8 +2128,9 @@ opsiyonel `ApprovalDateFieldName`, `ApprovalDescriptionFieldName`, `ApprovalIsFi dönsün). Onay düğümleri (`Criteria`) görsel akış tasarımcısında kurulur ve `ListFormWorkflow` kayıtlarına gider (§4.7). -**Adım 12 — Deploy.** Sol tarafta tüm adımların özeti, sağda üretim log'u. Üretim sırası — -seed dosyası uygulanırken `WizardDataSeeder`'ın izlediği sıranın aynısıdır: +**Adım 12 — Deploy.** Sol tarafta tüm adımların özeti, sağda üretim log'u. Log, deploy'un seed +dosyalarına yazdığı bölümleri sırayla gösterir; migrate'te `WizardDataSeeder` ekrana özgü kayıtları, +ardından `ListFormSeedDataApplier` menü/yetki/dil satırlarını kurar (§4.0): 1. Konfigürasyon doğrulama 2. Menü kaydı (`CreateMenu: false` ise atlanır) @@ -2134,10 +2139,11 @@ seed dosyası uygulanırken `WizardDataSeeder`'ın izlediği sıranın aynısıd 5. List yolunda: veri kaynağı → ListForm → alan grupları · Custom yolunda: bileşen bağlama 6. Deploy + tamamlandı -Başarıda sihirbaz `/admin/list/{ListFormCode}`'a yönlendirir ve seed dosyası -`configs/seeds/{kapsam}/wizard/` altına yazılmış olur. **Düzenleme modu** Wizard Manager'dan -`EditFileName` ile açılır: aynı adımlar dolu gelir, kaydetmede önce eski dosya ve -`InsertedRecords` kayıtları silinir (§4.5). +Başarıda sihirbaz "Migrate bekliyor" bildirimiyle Wizard Manager'a döner (ekran henüz kurulu +değildir; DB Migrate ile kurulur) ve seed dosyası `configs/seeds/{kapsam}/wizard/` altına yazılmış +olur. **Düzenleme modu** Wizard Manager'dan `EditFileName` ile açılır: aynı adımlar dolu gelir, +kaydetmede dosyalar cevaptan yeniden yazılır; önceki sürümün kayıtları `Pending.Replaced`'a alınır, +veritabanına dokunulmaz — kaldırma ve yeniden kurulum DB Migrate'tedir (§4.0). --- @@ -2471,7 +2477,7 @@ Preset mevcut JSON ile **birleşir**, diğer ayarları silmez; aynı anahtar var | TagBox anında seçim | `showSelectionControls`, `applyValueMode:instantly`, `searchEnabled` | | GridBox tek/çok seçim | `columns`, `selectionMode`, `filterRowVisible`, `height`, `width` | | Çoklu görsel | `width`, `height`, `multiple`, `accept:image/*` | -| HTML tam araç çubuğu | `toolbar.items` (tam liste), `toolbar.multiline`, `mediaResizing`, `imageUpload` | +| HTML tam araç çubuğu | `htmlEditorOptions.ts` varsayılanı: `toolbar.items` (tam liste), `toolbar.multiline`, `mediaResizing`, `imageUpload` (`tabs: file/url`, base64), `tableResizing`, `tableContextMenu` — boş `editorOptions` da aynı varsayılanı alır | | Yükseklik 100 / 200 | `{"height":100}` · `{"height":200}` | | Barkod okutma butonu | `buttons[]` — kamera ikonlu buton; `onClick` = `"@SCAN_BARCODE"` token'ı. Yalnız `dxTextBox`. Token'ı `editingForm.ts` kamera diyaloğuna bağlar, okunan kod alana yazılır. HTTPS şart. | | Kamerayla foto çek | `{"accept":"image/*","capture":"environment"}` — yalnız `dxImageUpload`. Mobilde dosya seçici yerine **yalnızca** kamera açılır (`user` = ön kamera); masaüstünde yok sayılır. Ayarlanmazsa dokunmatik cihazda dosya seçicinin yanına ayrı bir kamera butonu gelir, yani ikisi birden kullanılabilir — bu kalıp kamerayı **zorunlu** kılmak içindir. | @@ -2489,7 +2495,7 @@ Preset mevcut JSON ile **birleşir**, diğer ayarları silmez; aynı anahtar var | Telefon | `EditorOptions.Phone()` | global format + maske + örnek | | Kaydırıcı | `EditorOptions.Slider()` | `{"tooltip":{"enabled":true}}` | | Görsel yükleme | `EditorOptions.ImageUpload(multiple: true, 80, 80)` | | -| Zengin metin | `EditorOptions.Html(240)` | tam araç çubuğu + `encodeHtml` | +| Zengin metin | `EditorOptions.Html(240)` | `htmlEditorOptions.ts` varsayılanıyla aynı bloklar (tam araç çubuğu, `imageUpload`, `mediaResizing`, `tableResizing`, `tableContextMenu`; biri değişirse diğeri de değişir) + `encodeHtml` + `elementAttr` `min-height:240px` (sabit yükseklik değil; içerikle büyür) | Akıcı ekler: `.Placeholder()`, `.MaxLength()`, `.Height(int\|string)`, `.Width(int\|string)`, `.Format()`, `.FixedPointFormat(p)`, `.DisplayFormat()`, `.DateSerializationFormat()`, diff --git a/README.md b/README.md index ca081d54..010c7756 100644 --- a/README.md +++ b/README.md @@ -533,7 +533,7 @@ eskisi gibi çalışır. | `ComponentKind` | Üretilen | Menü URL'i | Yetkiler | | --- | --- | --- | --- | -| `List` | ListForm + ListFormField + Route + Menu + Permission + dil anahtarları | `/admin/list/{ListFormCode}` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` | +| `List` | ListForm + ListFormField (+ iş akışı) + Menu + Permission + dil anahtarları (rota üretilmez; genel `/admin/list/{ListFormCode}` rotası kullanılır) | `/admin/list/{ListFormCode}` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` | | `Custom` | Menü + Permission + dil anahtarları; ekran seçilen Custom Component'tir | Component'in `RoutePath` değeri | Yalnızca `.Create`, `.Update`, `.Delete` (Export/Import/Note ListForm'a özgüdür) | **Menüsüz wizard (`CreateMenu = false`).** Menü (ve gerekiyorsa üst menü) kaydı hiç üretilmez; @@ -542,7 +542,7 @@ ekranda SubGrid/parça olarak kullanılmak üzere tanımlanır. Alan varsayılan alanı taşımayan eski seed dosyaları etkilenmez. **İzin grubu seçimi.** Menü adımında izin grubu bir seçim listesinden gelir -(`GetWizardPermissionGroups`). Görünen adlar, grup adıyla aynı olan dil anahtarının metinlerinden +(`ListFormWizardAppService.GetPermissionGroups`, `/api/app/list-form-wizard/permission-groups`). Görünen adlar, grup adıyla aynı olan dil anahtarının metinlerinden okunur; seçim yapıldığında `PermissionGroupEn/Tr` forma doldurulur. Yeni bir grup tanımlanırsa grup adıyla aynı dil anahtarına bu metinler yazılır; mevcut grup seçilip alanlar boş bırakılırsa sunucu veritabanındaki değeri geri yazar. Böylece seed dosyası her durumda iki dilli @@ -556,12 +556,15 @@ Var olan bir ekranı düzenlemek için `/admin/listform/edit/{listFormCode}` — yapılandırması sekmeli bir editörde yönetilir (Veritabanı, Sütunlar, Filtreler, Düzenleme, Yetkiler, Alt Form, Pivot, Tree, Gantt, Scheduler, Todo, Widget, Workflow, Durum, Grafik sekmeleri). -**Wizard File Manager** (`/admin/listform/wizard` liste ekranı) üretilmiş seed dosyalarını yönetir: +**Wizard File Manager** (`/admin/listform/wizardManager`; sihirbazın kendisi `/admin/listform/wizard`) üretilmiş seed dosyalarını yönetir: liste/kart görünümü arasında geçiş (tercih diğer listelerle aynı yerde, `admin.lists.states` altında saklanır), arama, düzenleme (dosyanın yerine geçen yeni bir çalıştırma), silme, veritabanı migrate tetikleme, dışa aktarma ve içe aktarma. Liste, wizard'ın ilk deploy damgasına (`Wizard.SeededAt`) göre **tersine** sıralıdır — en son üretilen ekran en üstte durur; damgası -okunamayan dosyalar sona düşer. Butonlar `App.Listforms.Wizard.*` yetkileriyle gizlenir; asıl +okunamayan dosyalar sona düşer. Silme, bağlı wizard'ı olan dosyada reddedilir; onay diyaloğu önce +`DeleteAnalysis` ile düşürülebilecek tabloyu sorar, sonuç her kaydın akıbetiyle (`Deleted` / `Kept` + +kullananlar / `NotFound`) rapor olarak gösterilir (`WizardRecordCleaner`, `WizardTableCleaner`; +`lowcode.instructions.md` §4.5). Butonlar `App.Listforms.Wizard.*` yetkileriyle gizlenir; asıl kontrol `ListFormWizardAppService` üzerindedir. ### 7.7. İçe/dışa aktarma @@ -869,8 +872,8 @@ tıklamak seçim yapar, sayfayı değiştirmez — tıklama yakalama evresinde d **Diğer notlar.** `PlatformIcon` tasarımcı dokümanındaki ikon adını çözer; `selectComponents.ts` `Select.componentAs` için saklanan adı (`ReactSelect`, `CreatableSelect`, `AsyncSelect`) gerçek bileşene çevirir — kanvas ve üretilen bileşenin aynı adı aynı şekilde yorumlamasını sağlayan tek -nokta budur. Yeni bileşen `configs/seeds/host/data/App.DeveloperKit.CustomComponents/{Id}.json` -içindeki bir satır olarak başlar. +nokta budur. Yeni bileşen `configs/seeds/host/data/App.DeveloperKit.CustomComponents/` klasöründe +kendi `{Id}.json` satır dosyası olarak başlar. **Dinamik servis yaşam döngüsü:** `DynamicServiceCompiler` kodu derler → `DynamicAssemblyRegistrationService` assembly'yi tenant bağlamıyla kaydeder → @@ -917,8 +920,9 @@ kendi aralarında 1'den. `PlatformPermissionAppService` sıralama anahtarını k zincirinden kurar, böylece çocuk her zaman ebeveyninin altında kalır. Değerler seed dosyalarındaki `Order` alanında tutulur ve Permissions / Permission Groups ekranlarından düzenlenebilir. Wizard deploy'u yeni grubu ve grubun yeni kök yetkisini (seed satırı olarak) veritabanı ile seed -dosyasındaki kardeşlerin en büyük sırasının bir fazlasıyla, alt yetkileri Create/Update/Delete/Export/Import/Note dizilimiyle -1'den yazar (`NextPermissionOrder`, `AbpPermissionsExtensions`). +dosyasındaki kardeşlerin en büyük sırasının bir fazlasıyla, alt yetkileri `WizardSeedRowKeys.ChildPermissions` dizilimiyle +(List: Create/Update/Delete/Export/Import/Note, Custom: Create/Update/Delete) 1'den yazar +(`ListFormWizardAppService.WritePermissionGroupAsync` / `WritePermissionRowsAsync`). Aksiyon yetkisi olan alt gruplar (UI tarafındaki karşılıkları `constants/permission.constant.ts` içindedir; oradaki kontroller yalnızca butonları gizler, asıl kontrol AppService'lerdedir): @@ -948,7 +952,7 @@ içindedir; oradaki kontroller yalnızca butonları gizler, asıl kontrol AppSer | **Sozsoft.MailQueue** | Şablon (Scriban) tabanlı gövde üretimi, ek dosya yaşam döngüsü, kuyruk çalıştırma ve günlükleme. | | **Sozsoft.Sender** | Amazon SES (mail), Posta Güvercini (SMS), WhatsApp Cloud API, Rocket.Chat göndericileri. | | **Sozsoft.SqlQueryManager** | SQL nesne yönetimi, sorgu çalıştırma, tablo/kolon metadata'sı. | -| **Sozsoft.Mcp** | MCP (Model Context Protocol) sunucusu: `/mcp` Streamable HTTP ucu, kiracı bazlı araç seti, şema keşfi, sorgu, veri yazma ve (host) tablo/ekran/bileşen geliştirme. Kiracı kullanıcısı yalnızca veri okur. Bkz. §13.9. | +| **Sozsoft.Mcp** | MCP (Model Context Protocol) sunucusu: `/mcp` Streamable HTTP ucu, kiracı bazlı araç seti, şema keşfi, sorgu, veri yazma ve (host) tablo/ekran/bileşen geliştirme. Kiracı kullanıcısı veri okur ve ekranın C/U/D sözleşmesinden geçerek satır yazar; geliştirme araçları yalnızca host'tadır. Bkz. §13.9. | ### 10.2. Uygulama servisleri (`api/src/Sozsoft.Platform.Application`) @@ -1003,7 +1007,7 @@ DeveloperKit > Custom Components (Visual Designer) üzerinden değişir. Aynı y ile paylaşan custom component kazanır; karşılığı olan dosyası bulunmayan rota kaydı ise hiç yüklenmez (`hasPhysicalComponent`) — seeder yalnızca eklediği için eski kayıtlar veritabanında kalsa bile ekran patlamaz. Seed: `configs/seeds/host/data/App.DeveloperKit.CustomComponents/{Id}.json`; -bileşenin ayrı dosyası yoktur, her bileşen bu dosyada bir satırdır. +ekran klasör modundadır, her bileşen kendi satır dosyasındadır (dosya adı satırın `Id`'si). Runtime kapsamı sınırlıdır: bileşen kodundaki `import` satırları derlemede temizlenir, kullanılabilenler `React`, `ui` kit, `PlatformViewHost`, `apiService`, `axios`, `DOMPurify`, @@ -1089,7 +1093,7 @@ Veriden beslenen public sayfalar (`/products`, `/checkout`, `/payment`, `/succes | Parça | Nerede | Ne taşır | | --- | --- | --- | -| Genel varsayılanlar | `App.SiteManagement.Seo.*` ayarları (Ayarlar ekranı, yetki `App.SiteManagement.Seo`) | Site adresi, site adı, varsayılan başlık/açıklama/paylaşım görseli, Twitter hesabı, diller, robots yönergesi, Google doğrulaması, sosyal hesaplar, sitemap anahtarı | +| Genel varsayılanlar | `App.SiteManagement.Seo.*` ayarları (Ayarlar ekranı, yetki `App.SiteManagement.Seo`) | Site adresi, site adı, varsayılan başlık/açıklama/paylaşım görseli, Twitter hesabı, diller, robots yönergesi, Google doğrulaması, sosyal hesaplar, varsayılan anahtar kelimeler (`DefaultKeywords`), sitemap anahtarı (`SitemapEnabled`), sitemap dışı yollar (`SitemapExcludePaths`) | | Yol bazlı alanlar | Yolu tanımlayan kaydın `Seo*` kolonları: `Route` (`Sas_H_Route`, ekran **Menü Yönetimi › Route**) ve `CustomComponent` (`Sas_H_CustomComponent`, ekran **DeveloperKit › Custom Components**) | Yolun başlığı, açıklaması, anahtar kelimeleri, paylaşım görseli, kanonik adresi, `SeoNoIndex`, sitemap davranışı ve ek JSON-LD | | İstemci | `components/shared/SeoHead.tsx` | `title`, `description`, `robots`, `canonical`, `og:`/`twitter:` kartı, `og:locale:alternate` ve JSON-LD | @@ -1109,7 +1113,11 @@ Sayfanın kendi `SeoHead`'i, kabuğun (`PublicLayout`) `fallback` kaydını her `sitemap.xml` ve `robots.txt` aynı servisten üretilir (`SeoController`, anonim). Sitemap'in kaynakları: `Pub` menüleri, aktif custom component rotaları, yayınlanmış blog yazıları ve yolu tanımlayan kayıtların `Seo*` alanları; `/admin` yolları, parametreli rotalar (`/blog/:id`) ve -`SeoNoIndex` işaretli yollar elenir. Arama motorları bu iki dosyayı **sitenin** kök dizininde aradığı için UI sunucusu her iki +`SeoNoIndex` işaretli ya da `SeoIncludeInSitemap = false` olan yollar elenir. +`App.SiteManagement.Seo.SitemapExcludePaths` (satır başına bir yol, sonu `*` ise önek) hem sitemap'ten +düşer hem `robots.txt`'e `Disallow` olarak yazılır; `robots.txt` ayrıca `/admin/`, `/auth/`, +`/checkout`, `/payment`, `/success` yollarını her zaman kapatır. `SitemapEnabled` kapalıyken +`sitemap.xml` 404 döner ve robots'a `Sitemap:` satırı yazılmaz. Arama motorları bu iki dosyayı **sitenin** kök dizininde aradığı için UI sunucusu her iki yolu da API'ye geçirir (`vite.config.ts` > `seoProxy`, hem `server` hem `preview`); ayrı bir ters vekil kuralı gerekmez. `VITE_API_URL` tanımlı değilse proxy kurulmaz ve `ui/public/robots.txt` içindeki statik varsayılan geçerli kalır. @@ -1276,21 +1284,23 @@ Bildirimin tamamı Administration menüsünde `App.Administration.Notifications` **Bildirim Türleri**, **Bildirim Kuralları**, **Bildirimler**, **Bildirim Şablonları**. Grup Agenda'nın üstündedir; Saas altında bildirim ekranı **yoktur**. -Tablolar kiracı kapsamındadır (`Adm_T_NotificationType`, `Adm_T_NotificationRule`, -`Adm_T_Notification`; üçü de `IMultiTenant`). Her kiracı kendi türünü, kuralını ve kuyruğunu +Tablolar kiracı kapsamındadır (`Adm_T_NotificationTemplate`, `Adm_T_NotificationType`, +`Adm_T_NotificationRule`, `Adm_T_Notification`; dördü de `IMultiTenant`). Her kiracı kendi türünü, kuralını ve kuyruğunu görür; host kaydı (`TenantId = null`) yalnızca host oturumunda görünür. Yeni bir kiracı oluşturulduğunda varsayılan tür ve kural kümesi o kiracıya kopyalanır (`NotificationTenantDefaultsSeedContributor`): kaynak host kapsamındaki `data/App.Notifications.Notification{Types,Rules}.json` dosyalarıdır, satırlar **yeni -kimliklerle** yazılır ve eşleşme ad üzerinden yapıldığı için ikinci çalıştırma bir şey eklemez. +kimliklerle** yazılır ve eşleşme ad üzerinden yapıldığı için ikinci çalıştırma bir şey eklemez; +şablonlar kopyalanmaz. **Bildirimler** ekranı (`App.Notifications.Notification`) hem kuyruk günlüğü hem gönderim ekranıdır: liste kural motorunun ürettiklerini de gösterir, ekleme formu ise elle gönderimdir. Ayrı bir manuel gönderim kaydı yoktur — gönderimin kaydı bildirimin kendisidir. Ekleme anında yazma işi `list-form-dynamic-api/notification-insert` ucuna gider (`InsertServiceAddress`); uç seçilen alıcı × kanal açılımını `INotificationManager.SendManualNotificationAsync` ile kuyruğa -yazar — yani Bildirim Kuralları ekranındaki elle gönderimle **aynı yol**, tek fark alıcı -kümesinin kurallardan değil ekrandan gelmesi. Güncelleme kapalıdır, silme kaydı kaldırır. +yazar. Bildirim Kuralları ekranındaki `CreateNotification` diyaloğu ise aynı yöneticinin +`SendNotificationAsync` yolunu kullanır: türün kurallarını işletir ve kullanıcı tercihini dikkate +alır; bu uç alıcıyı ekrandan alır ve tercihi okumaz. Güncelleme kapalıdır, silme kaydı kaldırır. Formdaki `NotificationChannel` ve `UserId` alanları listede tek değerlidir (her satır bir alıcı × kanal), formda `dxTagBox` ile çoklu seçilir; tipli DTO yolunda çoklu değeri @@ -1414,12 +1424,14 @@ liste süzgeci ve çağrı kapısı aynı kuralı kullanır. | `save_dev_request_analysis` | `Backlog` ya da `Analysis` durumundaki talebin analizini (HTML) `Analysis` kolonuna yazar — var olanı değiştirir — ve talebi `Analysis` durumuna alır; diğer durumları reddeder. `Description`'a dokunmaz. Analizin standardı `lowcode.instructions.md` §0.12. `.Write` + `App.DevRequests.Update` ister. | | `add_dev_request_subtasks` | Talebin `SubTasks` listesine tamamlanmış satırlar ekler: model yalnızca başlığı verir, atanan kişi bağlanan kullanıcının adı (`userName`), bitiş tarihi bugün (`yyyy-MM-dd`) olarak sunucuda yazılır; var olan satırlar korunur. `.Write` + `App.DevRequests.Update` ister. | | `append_dev_request_note` | Talebin `Analysis` kolonunun sonuna HTML ekler (üretim sonuç notu); `Description` talebin yazıldığı hâliyle kalır. İkisi de `update_row` ile yazılmaz — açıklama base64 görsel taşır. `.Write` + `App.DevRequests.Update` ister. | +| `list_components` / `get_component` / `save_component` / `delete_component` | Custom component'ler `ICustomComponentAppService` üzerinden; seed senkronu ve `DataSources` çözümü aynen çalışır. `save_component` ada göre ekler/günceller, tasarımcı belgesi (`Props.visualDesigner`, `sourceMode ≠ code`) taşıyan bileşene kod yazmayı reddeder — kod belgeden üretildiği için tasarımcıdaki ilk kayıtta ezilirdi. | **Timesheets.** Talebin `Timesheets` listesini sunucu doldurur (`IMcpDevRequestTimesheetRecorder`): `get_dev_request` / `start_dev_request` talebi kullanıcının etkin talebi yapar (dağıtık önbellek, `McpDevRequestActivityCacheItem`, 12 saat); sonraki her MCP yazma işlemi (`create_table`, `deploy_wizard`, `delete_wizard`, `save_component`, `delete_component`, satır araçları, -`save_dev_request_analysis`) o talebe bir satır ekler. Açıklama `App.Mcp.Timesheet.*` anahtarından +`save_dev_request_analysis`, `append_dev_request_note`, `add_dev_request_subtasks`) o talebe bir satır +ekler; talebi açan `start_dev_request` yazmaz. Açıklama `App.Mcp.Timesheet.*` anahtarından Türkçe üretilir, süre önceki satırdan ya da talebin açıldığı andan ölçülür (en az 1 dakika, saat olarak), çalışan bağlanan kullanıcı, tarih bugündür. Etkin talep yoksa satır yazılmaz; bozuk bir `Timesheets` değeri işlemi geri almaz, yalnızca satır atlanır ve log'a düşer. @@ -1454,7 +1466,6 @@ değilse sunucu reddeder ve hiçbir şey üretilmez. başlıkta komut yerine Id ya da düz metin kullan. Başlık birden fazla talebe uyuyorsa sunucu Id ister. - Düz metin de aynı yoldan geçer: sunucu talimatları modeli `start_dev_request`'e yönlendirir, durum şartı yine sunucuda uygulanır. -| `list_components` / `get_component` / `save_component` / `delete_component` | Custom component'ler `ICustomComponentAppService` üzerinden; seed senkronu ve `DataSources` çözümü aynen çalışır. `save_component` ada göre ekler/günceller, tasarımcı belgesi (`Props.visualDesigner`, `sourceMode ≠ code`) taşıyan bileşene kod yazmayı reddeder — kod belgeden üretildiği için tasarımcıdaki ilk kayıtta ezilirdi. | **Yetki modeli: MCP yetki vermez.** Kullanıcı MCP'de uygulamadaki yetkisini taşır; MCP'nin iki yetkisi yalnızca kapıdır. Bir çağrı üç katmandan geçer ve her katman ayrı reddeder: @@ -1471,7 +1482,7 @@ yetkisi yalnızca kapıdır. Bir çağrı üç katmandan geçer ve her katman ay | Yetki | Kim alabilir | Ne açar | Açmadığı / sınırı | | --- | --- | --- | --- | | `App.DeveloperKit.McpServers` | Host, kiracı | MCP Servers ekranını görme (`.Create/.Update/.Delete/.Export/.Import` ekran işlemleri) | `/mcp` ucuna erişim vermez | -| `App.DeveloperKit.McpServers.Invoke` | Host, kiracı | `/mcp` bağlantısı ve bütün okuma araçları: `server_info`, `list_lists`, `describe_list`, `search_schema`, `query_list`; host'ta ayrıca `get_dev_guide`, `list_wizards`, `get_wizard`, `verify_wizard`, `list_components`, `get_component` | Hiçbir şey yazmaz. Okuma da ekran yetkisine bağlıdır: `R` yetkisi olmayan liste görünmez, sorgulanamaz. Ham SQL (`run_select`, `list_queries`, `run_query`) ayrıca `App.SqlQueryManager` ister | +| `App.DeveloperKit.McpServers.Invoke` | Host, kiracı | `/mcp` bağlantısı ve bütün okuma araçları: `server_info`, `list_lists`, `describe_list`, `search_schema`, `query_list`; host'ta ayrıca `get_dev_guide`, `list_wizards`, `get_wizard`, `verify_wizard`, `get_dev_request`, `list_components`, `get_component` | Hiçbir şey yazmaz. Okuma da ekran yetkisine bağlıdır: `R` yetkisi olmayan liste görünmez, sorgulanamaz. Ham SQL (`run_select`, `list_queries`, `run_query`) ayrıca `App.SqlQueryManager` ister | | `App.DeveloperKit.McpServers.Write` | Host, kiracı | MCP üzerinden her değişiklik. **Kiracıda:** `insert_row`, `update_row`, `delete_row`. **Host'ta:** bunlara ek olarak `create_table`, `deploy_wizard`, `delete_wizard`, `save_component`, `delete_component`, `start_dev_request`, `save_dev_request_analysis`, `append_dev_request_note`, `add_dev_request_subtasks` | Kendi başına bir şey yazdırmaz: satır araçları ekranın `C`/`U`/`D` yetkisini, geliştirme araçları Wizard / Custom Components / SQL Query Manager / `App.DevRequests.Update` yetkisini ayrıca ister. Kiracıda geliştirme araçlarını **açmaz** — kiracı bağlamında bu araçlar yetkiden bağımsız kapalıdır (`McpToolNames.TenantTools`). `Invoke` olmadan işe yaramaz | **Araç → gereken yetkiler** (Invoke hepsinde aranır, tabloda tekrar yazılmadı) @@ -1960,7 +1971,7 @@ configs/seeds/ │ │ # Abp.Identity.Permission{Groups,s}.json — yetki tanımları │ │ # App.Definitions.{CountryGroup,Country,City,District}.json — adres zinciri │ │ # App.Definitions.{Currency,ContactTitle,AiBot}.json — para birimi, hitap, AI bot -│ │ # App.Notifications.Notification{Types,Rules}.json — bildirim tipi → kuralı +│ │ # App.Notifications.Notification{Templates,Types,Rules}.json — bildirim şablonu, tipi → kuralı │ │ # App.Languages.{Language,LanguageText}.json — dil → metin (eksik LanguageKey metinle üretilir) │ │ # App.SettingDefinitions.json, App.BackgroundWorkers.RecurringJobs.json — ayar tanımları, zamanlanmış işler │ ├── sql/{object,execute}/ # .sql (SQL Server) @@ -1970,8 +1981,9 @@ configs/seeds/ ``` > Bu dosyalar elle de düzenlenebilir; ilgili ekrandan tekrar kaydedildiğinde yeniden üretilirler. -> Wizard dosyası `EditFileName` ile güncellenirse sunucu önce eski dosyayı ve ürettiği kayıtları -> siler, sonra yenisini üretir. Wizard'ın ürettiği ekran **ListForm detayından** düzenlendiğinde +> Wizard dosyası `EditFileName` ile güncellenirse deploy veritabanına dokunmaz: dosyalar cevaptan +> yeniden yazılır (ad değiştiyse eski dosya silinir), önceki sürümün kayıtları `Pending.Replaced`'a +> alınır ve DB Migrate onları kaldırıp ekranı yeniden kurar (`lowcode.instructions.md` §4.0). Wizard'ın ürettiği ekran **ListForm detayından** düzenlendiğinde > de aynı dosya güncellenir (`WizardSeedSynchronizer`): `ListForm`/`Fields` bölümleri ve > `Wizard` bölümündeki karşılıkları veritabanındaki güncel haliyle değiştirilir. diff --git a/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Application/McpDevelopAppService.cs b/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Application/McpDevelopAppService.cs index 338a8999..bf66953b 100644 --- a/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Application/McpDevelopAppService.cs +++ b/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Application/McpDevelopAppService.cs @@ -448,6 +448,7 @@ public class McpDevelopAppService( var devRequest = await FindDevRequestAsync(request); devRequest.Analysis = string.Concat(devRequest.Analysis, html.Trim()); await devRequestRepository.UpdateAsync(devRequest, autoSave: true); + await timesheetRecorder.RecordAsync(McpTimesheetTexts.AppendNote); } /// @@ -497,6 +498,7 @@ public class McpDevelopAppService( devRequest.SubTasks = subTasks.ToJsonString(); await devRequestRepository.UpdateAsync(devRequest, autoSave: true); + await timesheetRecorder.RecordAsync(McpTimesheetTexts.AddSubTasks, items.Count); } private static McpDevRequestDto ToDto(DevRequest devRequest) diff --git a/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Domain.Shared/McpTimesheetTexts.cs b/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Domain.Shared/McpTimesheetTexts.cs index 8ebfc23d..7400b6d3 100644 --- a/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Domain.Shared/McpTimesheetTexts.cs +++ b/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Domain.Shared/McpTimesheetTexts.cs @@ -9,6 +9,8 @@ public static class McpTimesheetTexts private const string Prefix = "App.Mcp.Timesheet."; public const string Analysis = Prefix + "Analysis"; + public const string AppendNote = Prefix + "AppendNote"; + public const string AddSubTasks = Prefix + "AddSubTasks"; public const string CreateTable = Prefix + "CreateTable"; public const string DeployWizard = Prefix + "DeployWizard"; public const string DeleteWizard = Prefix + "DeleteWizard"; diff --git a/api/src/Sozsoft.Platform.Domain.Shared/Editors/EditorOptions.cs b/api/src/Sozsoft.Platform.Domain.Shared/Editors/EditorOptions.cs index 63d04709..a9d741dd 100644 --- a/api/src/Sozsoft.Platform.Domain.Shared/Editors/EditorOptions.cs +++ b/api/src/Sozsoft.Platform.Domain.Shared/Editors/EditorOptions.cs @@ -80,71 +80,57 @@ public static class EditorOptions /// /// height verilirse editor sabitlenir ve icerik kaydirilir; min-height ise /// en az yuksekligi korurken yeni satirlarla buyumesine izin verir. + /// Bloklar UI'daki htmlEditorOptions.ts varsayilaniyla birebir aynidir: istemci ic ice + /// bloklari tek seviye birlestirdigi icin buradaki toolbar varsayilanin yerine gecer. /// public static EditorOptionsBuilder Html(int minHeight = 200) => New() - .Raw("mediaResizing", "{\"enabled\":true}") - .Raw("imageUpload", "{\"tabs\":[\"file\",\"url\"],\"fileUploadMode\":\"base64\"}") .Raw("toolbar", "{\"multiline\":true,\"items\":[" + string.Join(",", HtmlToolbarItems()) + "]}") + .Raw("imageUpload", "{\"tabs\":[\"file\",\"url\"],\"fileUploadMode\":\"base64\"}") + .Raw("mediaResizing", "{\"enabled\":true}") + .Raw("tableResizing", "{\"enabled\":true}") + .Raw("tableContextMenu", "{\"enabled\":true}") .Flag("encodeHtml", true) .Raw("elementAttr", "{\"style\":\"min-height:" + EditorJson.Number(minHeight) + "px\"}"); - private static IEnumerable HtmlToolbarItems() - { - var fontSizes = new[] { "8pt", "10pt", "12pt", "14pt", "18pt", "24pt", "36pt" }; - var fontFamilies = new[] - { - "Arial", "Courier New", "Georgia", "Impact", - "Lucida Console", "Tahoma", "Times New Roman", "Verdana" - }; + private static readonly string[] HtmlToolbarNames = + [ + "undo", "redo", "separator", + "header", "size", "font", "separator", + "bold", "italic", "underline", "strike", "superscript", "subscript", "separator", + "color", "background", "separator", + "alignLeft", "alignCenter", "alignRight", "alignJustify", "separator", + "orderedList", "bulletList", "decreaseIndent", "increaseIndent", "separator", + "blockquote", "codeBlock", "link", "image", "separator", + "insertTable", "insertHeaderRow", "insertRowAbove", "insertRowBelow", "deleteRow", + "insertColumnLeft", "insertColumnRight", "deleteColumn", "cellProperties", "tableProperties", "deleteTable", + "separator", "clear" + ]; - yield return Item("undo"); - yield return Item("redo"); - yield return Item("separator"); - yield return Picker("size", fontSizes, "Font size"); - yield return Picker("font", fontFamilies, "Font family"); - yield return Item("separator"); - yield return Item("bold"); - yield return Item("italic"); - yield return Item("strike"); - yield return Item("underline"); - yield return Item("separator"); - yield return Item("alignLeft"); - yield return Item("alignCenter"); - yield return Item("alignRight"); - yield return Item("alignJustify"); - yield return Item("separator"); - yield return Item("orderedList"); - yield return Item("bulletList"); - yield return Item("separator"); - yield return "{\"name\":\"header\",\"acceptedValues\":[false,1,2,3,4,5]}"; - yield return Item("separator"); - yield return Item("color"); - yield return Item("background"); - yield return Item("separator"); - yield return Item("link"); - yield return Item("image"); - yield return Item("separator"); - yield return Item("clear"); - yield return Item("codeBlock"); - yield return Item("blockquote"); - yield return Item("separator"); - yield return Item("insertTable"); - yield return Item("deleteTable"); - yield return Item("insertRowAbove"); - yield return Item("insertRowBelow"); - yield return Item("deleteRow"); - yield return Item("insertColumnLeft"); - yield return Item("insertColumnRight"); - yield return Item("deleteColumn"); - yield return Item("cellProperties"); - yield return Item("tableProperties"); - } + private static readonly string[] HtmlFontSizes = ["8pt", "10pt", "12pt", "14pt", "18pt", "24pt", "36pt"]; + + private static readonly string[] HtmlFontFamilies = + [ + "Arial", "Courier New", "Georgia", "Impact", + "Lucida Console", "Segoe UI", "Tahoma", "Times New Roman", "Verdana" + ]; + + private static readonly string[] HtmlHeaderValues = ["false", "1", "2", "3", "4", "5", "6"]; + + private static IEnumerable HtmlToolbarItems() + => HtmlToolbarNames.Select(name => name switch + { + "size" => Picker(name, HtmlFontSizes.Select(EditorJson.Quote), "Font size"), + "font" => Picker(name, HtmlFontFamilies.Select(EditorJson.Quote), "Font family"), + "header" => Picker(name, HtmlHeaderValues, "Header"), + _ => Item(name) + }); private static string Item(string name) => "{\"name\":" + EditorJson.Quote(name) + "}"; + /// JSON olarak yazilmis degerlerdir (metin tirnakli, sayi/bool ciplak). private static string Picker(string name, IEnumerable values, string ariaLabel) => "{\"name\":" + EditorJson.Quote(name) + - ",\"acceptedValues\":[" + string.Join(",", values.Select(EditorJson.Quote)) + "]" + + ",\"acceptedValues\":[" + string.Join(",", values) + "]" + ",\"options\":{\"inputAttr\":{\"aria-label\":" + EditorJson.Quote(ariaLabel) + "}}}"; } diff --git a/api/src/Sozsoft.Platform.Domain/Extensions/AbpPermissionsExtensions.cs b/api/src/Sozsoft.Platform.Domain/Extensions/AbpPermissionsExtensions.cs index 6964ca01..db05ca39 100644 --- a/api/src/Sozsoft.Platform.Domain/Extensions/AbpPermissionsExtensions.cs +++ b/api/src/Sozsoft.Platform.Domain/Extensions/AbpPermissionsExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using Volo.Abp.Data; using Volo.Abp.PermissionManagement; @@ -49,20 +48,5 @@ public static class AbpPermissionsExtensions .Distinct() .ToArray(); } - - /// - /// Yetki ekranindaki sira () kardesler arasindadir; - /// yeni kayit en buyuk kardes sirasinin bir fazlasini alir, kardes yoksa 1. Bos birakilan sira - /// 0 olur ve kayit listenin en ustune cikar. - /// - public static int NextPermissionOrder(this IEnumerable siblings) - { - return siblings.Select(s => s.GetProperty(PlatformConsts.Permissions.Order)).DefaultIfEmpty(0).Max() + 1; - } - - public static void SetPermissionOrder(this IHasExtraProperties record, int order) - { - record.SetProperty(PlatformConsts.Permissions.Order, order); - } } diff --git a/claude.md b/claude.md index fbd462aa..48224370 100644 --- a/claude.md +++ b/claude.md @@ -176,14 +176,19 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` → - **`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. +- **`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ında tekrar yok** → `Wizard` bölümü yalnızca ekrandan üretilemeyen bilgileri taşır. `ListForm`/`Fields` içinde karşılığı olan her alan (veri kaynağı, select, anahtar alan, kırılım/düzen/düzenleme bayrakları, görünüm option blokları, alt form, widget, iş akışı, alan tanımları) dosyaya yazılmadan çıkarılır (`WizardSeedFileDto.ToSeedJson`) ve okurken geri doldurulur (`FromSeedJson`). Wizard'a yeni bir alan eklerken karşılığı ListForm'da varsa listeye ekle. Dosyada kalan wizard alanları üç başlık altında toplanır — `Menu`, `Language`, `Groups` — ve - `ListFormCode` → `WizardCode`, `MenuParentModuleId` → `ModuleId` adıyla yazılır; varsayılanı olan - alanlar (`CreateMenu: true`, boş `DataSourceConnectionString`) hiç yazılmaz. Bu yalnızca dosya biçimidir; C# + `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. 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ı bloklu bölümleri sessizce düşürür. @@ -264,8 +269,10 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` → `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 MCP yazma işlemi ona `App.Mcp.Timesheet.*` anahtarından Türkçe açıklama ve ölçülen - süreyle bir satır ekler. Yeni bir MCP yazma aracı eklenirse kaydedici çağrısı ve dil anahtarı da eklenir. + sonraki her üretim/veri yazma işlemi (`create_table`, `deploy_wizard`, `delete_wizard`, + `save_component`, `delete_component`, satır araçları, `save_dev_request_analysis`, + `append_dev_request_note`, `add_dev_request_subtasks`) ona `App.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. - **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` @@ -289,8 +296,8 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` → karşılık gelen yetkiyle eklenir. `create_table` bu yüzden `.Write`'a ek olarak `App.SqlQueryManager` ister; tablo scriptini tasarımcının `generateCreateTableSql` çıktısıyla aynı biçimde üretir (`McpTableCreator`) — tasarımcının şablon kolonları ya da script biçimi değişirse o da değişir. -- **Bildirim kapsamı** → bildirim tipi, kuralı ve kuyruğu kiracıya aittir (`Adm_T_NotificationType`, - `Adm_T_NotificationRule`, `Adm_T_Notification`; üçü de `IMultiTenant`) ve ekranların tamamı +- **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 diff --git a/configs/seeds/host/data/App.Languages.LanguageText.json b/configs/seeds/host/data/App.Languages.LanguageText.json index 038d6dd6..5f5a0aaf 100644 --- a/configs/seeds/host/data/App.Languages.LanguageText.json +++ b/configs/seeds/host/data/App.Languages.LanguageText.json @@ -67428,6 +67428,34 @@ "Key": "App.Mcp.Timesheet.DeleteRow", "Value": "{0} ekranından satır silindi" }, + { + "Id": "02d139fc-753d-567a-b672-8aa0a0212ad0", + "CultureName": "en", + "ResourceName": "Platform", + "Key": "App.Mcp.Timesheet.AppendNote", + "Value": "Production note added to the analysis" + }, + { + "Id": "9e9cc3eb-557b-5a02-8730-29347f4f77b4", + "CultureName": "tr", + "ResourceName": "Platform", + "Key": "App.Mcp.Timesheet.AppendNote", + "Value": "Analize üretim notu eklendi" + }, + { + "Id": "eb67cbd2-c6b3-57d6-ab44-ee7e79636553", + "CultureName": "en", + "ResourceName": "Platform", + "Key": "App.Mcp.Timesheet.AddSubTasks", + "Value": "{0} completed subtasks added" + }, + { + "Id": "ec94c375-713b-51f8-8db8-5e58b9e4ce7b", + "CultureName": "tr", + "ResourceName": "Platform", + "Key": "App.Mcp.Timesheet.AddSubTasks", + "Value": "{0} tamamlanmış alt görev eklendi" + }, { "Id": "a5e491f7-a85c-572f-bf66-4245cb84fa36", "CultureName": "en", diff --git a/ui/src/views/shared/htmlEditorOptions.ts b/ui/src/views/shared/htmlEditorOptions.ts index 6ad2a383..95bac9da 100644 --- a/ui/src/views/shared/htmlEditorOptions.ts +++ b/ui/src/views/shared/htmlEditorOptions.ts @@ -3,6 +3,7 @@ * `` ile, dinamik formlar `mergeHtmlEditorOptions` ile * aynı yetenek setini alır; ListForm'daki "HTML toolbar" kalıbı da buradan beslenir. * Değerler JSON'a yazılabilir kalmalıdır — kalıp editorOptions kolonuna olduğu gibi kaydedilir. + * Seed'li ekranlar aynı blokları C# `EditorOptions.Html` ile yazar; biri değişirse diğeri de değişir. */ import type { Properties as HtmlEditorProperties } from 'devextreme/ui/html_editor'