TodoBoard kısmına Analiz sütunu eklendi
This commit is contained in:
parent
a0f359df67
commit
f0e1280717
33 changed files with 1065 additions and 138 deletions
|
|
@ -414,12 +414,20 @@ SELECT 'Onaylı', COUNT(*), 'green', 'FaCheck' FROM Mrp_T_Order WHERE Status = 2
|
|||
Sorgu ekranın bağlantısında (`IsTenant` + `DataSourceCode` ile çözülen, `lowcode` §9.3.3),
|
||||
**olduğu gibi** çalıştırılır:
|
||||
|
||||
- Ekranın otomatik tenant/şube/OU filtresi **uygulanmaz**.
|
||||
- `@USERID` / `@TENANTID` gibi token'lar **çözülmez** — bunlar `*FieldsDefaultValueJson` ve
|
||||
`DefaultFilter` yolunun sözleşmesidir. Widget sorgusuna yazılırsa bağlanmamış parametre
|
||||
hatası verir. Kiracı kırılımı gerekiyorsa ya kiracıya özel bir `DataSource` kullan, ya da
|
||||
kırılımı sorgunun okuduğu view'ın içine göm.
|
||||
- Soft delete'li tabloda `"IsDeleted" = 'false'` koşulunu sorguya kendin yaz.
|
||||
- Ekranın otomatik tenant/şube/OU filtresi **uygulanmaz**; kırılım sorguya yazılır.
|
||||
- Sorgu `DefaultFilter` ile aynı token çözümünden geçer (`DefaultValueHelper.GetDefaultValue`):
|
||||
`@USERID`, `@USERNAME`, `@DATE`, `@NOW` ve `@TENANTID` metin olarak yerine konur.
|
||||
Kiracı tablosunda (`IsTenant: true`) her karta `"TenantId" = '@TENANTID'` yaz; host'ta bu
|
||||
`"TenantId" IS NULL`'a döner, yani kartlar liste ekranıyla aynı kümeyi sayar. Yazılmazsa ortak
|
||||
veritabanında kartlar bütün kiracıların toplamını gösterir.
|
||||
- `IsDeleted` koşulu widget sorgusuna yazılmaz (platform varsayılanı).
|
||||
- Wizard / MCP ile üretilen List ekranı sorulmadan en az 3 kartla gelir; kart seçimi, istisnalar
|
||||
ve metin/renk kuralı: `lowcode` §4.6.1.
|
||||
- Seeder'da şerit `DefaultWidgetsJson(tablo, listFormName, tenant, params WidgetCard[])` ile
|
||||
kurulur (`ListFormSeeder_DefaultJsons`): kart başına `Title` (dil anahtarı), renk, ikon, ek koşul
|
||||
ve isteğe bağlı `Value` ifadesi; alt başlık ekranın adıdır, değer tip çakışmasın diye metne
|
||||
çevrilir. Gruplamalı sorgu için `DefaultWidgetsJson(sql, kartSayısı)`. Genel kart başlıkları
|
||||
`App.Widget.*` dil anahtarlarındadır.
|
||||
|
||||
### 5.4 `LayoutJson` (`LayoutDto`)
|
||||
|
||||
|
|
@ -547,7 +555,7 @@ yazılırsa düşer), `AllowTaskAdding`, `AllowTaskUpdating`, `AllowTaskResource
|
|||
|
||||
### 7.4 `TodoOptionJson` (`TodoOptionDto`) — Kanban
|
||||
|
||||
`TitleExpr`*, `StatusExpr`*, `DescriptionExpr`, `DueDateExpr`, `TagExpr`, `AssigneeExpr`,
|
||||
`TitleExpr`*, `StatusExpr`*, `DescriptionExpr`, `AnalysisExpr` (detayda Açıklama yanında HTML sekmesi), `DueDateExpr`, `TagExpr`, `AssigneeExpr`,
|
||||
`PriorityExpr`, `CompletedExpr`, `OrderExpr`, `TimesheetExpr`, `SubTaskExpr`,
|
||||
`StatusOrder` (kolon sırası, virgüllü), `AllowDragging` (varsayılan `true`).
|
||||
|
||||
|
|
@ -1249,7 +1257,7 @@ soneklerini alır.
|
|||
| **Public › Sales Orders** | `App.Orders.SalesOrders` (alt form Sales Order Item) | `Sas_H_Order` + `Sas_H_OrderItem` | host | **Onay akışı + alt kalem + toplam + diyalog** birlikte kullanılan tam örnek (§12.6). |
|
||||
| **Public › Blog Category / Posts** | `App.BlogManagement.Category` · `.Posts` | `Sas_H_BlogCategory` · `Sas_H_BlogPost` | host | Çok dilli içerik (`ContentTr`/`ContentEn`), slug, yayın durumu, sayaç alanları; public `/blog` bu tablolardan okur, yayınlanmış yazılar sitemap'e girer. |
|
||||
| **Public › Demos** | `App.Demos` | `Sas_H_Demo` | host | Public `/demo` formundan gelen talepler — public form → tablo akışının örneği. |
|
||||
| **Dev Requests** | `App.DevRequests` | `Sas_H_DevRequest` | host | **Kanban/Todo düzeninin** referansı (`DefaultLayout: todo`): `Status`, `Priority`, `SortOrder`, `Tags`, `Assignees`, `SubTasks`, `Timesheets`. MCP geliştirme döngüsünün kuyruğudur (`Ready to Start` → `Testing`, `Staging`'e kullanıcı alır; araç çubuğunda DB Migrate butonu vardır — `UiEvalService.ApiDbMigrate()`, `App.Setup.Migrate`; yalnızca `start_dev_request` başlatır; açıklamadaki base64 görseller MCP'ye resim içeriği olarak gider, not `append_dev_request_note` ile eklenir; `lowcode.instructions.md` §0.11). |
|
||||
| **Dev Requests** | `App.DevRequests` | `Sas_H_DevRequest` | host | **Kanban/Todo düzeninin** referansı (`DefaultLayout: todo`): `Status`, `Priority`, `SortOrder`, `Tags`, `Assignees`, `SubTasks`, `Timesheets`; detayda `Description` yanında `Analysis` sekmesi (`AnalysisExpr`). MCP geliştirme döngüsünün kuyruğudur (`Backlog` → `Analysis` MCP analiz eder, `Ready to Start`'a kullanıcı alır; `Ready to Start` → `Testing`, `Staging`'e kullanıcı alır; araç çubuğunda DB Migrate butonu vardır — `UiEvalService.ApiDbMigrate()`, `App.Setup.Migrate`; yalnızca `start_dev_request` başlatır; açıklamadaki base64 görseller MCP'ye resim içeriği olarak gider, analiz `save_dev_request_analysis` ile yazılır, üretim notu `append_dev_request_note` ile `Analysis`'e eklenir — `Description` talebin yazıldığı hâliyle kalır; `lowcode.instructions.md` §0.11–§0.12). |
|
||||
| **Tenants** | `AbpTenantManagement.Tenants` | `AbpTenants` | host | Kiracı kaydı, bağlantı dizesi diyaloğu (`TenantsConnectionString`), alt form olarak şubeler; yazma `list-form-dynamic-api/tenant-*`. Ekranların `IsTenant` davranışının kaynağı. |
|
||||
| **Branches** | `App.Branches` | `Sas_T_Branch` | tenant | Şube listesi. Ekranı `IsBranch = true` yaparsan filtre bu tablodan gelir; `BranchSeed` diyaloğu şube verisini kurar. |
|
||||
| **Setting Definitions** | `App.SettingDefinitions` | `Sas_H_SettingDefinition` | host | Yeni uygulama ayarı: `Code`, `MainGroupKey`/`SubGroupKey`, `DataType`, `SelectOptions`, `DefaultValue`, `Providers`, `RequiredPermissionName`, `IsEncrypted`, `IsVisibleToClients`. Hazır tanımlar `data/App.SettingDefinitions.json`'dan gelir. Ayarlanabilir olması gereken değeri koda gömme. |
|
||||
|
|
@ -1446,7 +1454,7 @@ içinde ilgili `#region` altındadır.
|
|||
| Tek kayıtlık form ekranı | `Licence` (`ListFormType = Form` + alt kalem listesi) |
|
||||
| Onay akışı | `Sales Orders` — `WorkflowJson` (`ApprovalUserFieldName`, `ApprovalStatusFieldName`, `ApprovalDateFieldName`, `ApprovalDescriptionFieldName`) + `ListFormWorkflow` düğümleri (Start → Approval → End/End) |
|
||||
| Alt form (master-detail) | `Tenant → Branches`, `Sales Orders → Sales Order Item`, `Survey → Question → Option` |
|
||||
| KPI widget | `User`, `Dynamic Services`, `Custom Components` — `WidgetsJson`; tek sorgu `UNION ALL` ile birden çok kart döndürür (`Title`, `Value`, `Color`, `SubTitle`, `Icon`) |
|
||||
| KPI widget | Saas ve Administration'daki yönetim ekranlarının çoğu (Users, Tenants, Dynamic Services, Custom Components, Audit Logs, Bildirimler, Duyuru/Anket/Etkinlik, Forum…; basit tanım listeleri hariç) — `WidgetsJson`; tek sorgu `UNION ALL` ile birden çok kart döndürür (`Title`, `Value`, `Color`, `SubTitle`, `Icon`) |
|
||||
| Toolbar'dan diyalog açma | `Tenant` (`TenantsConnectionString`), `Role` (`RolesPermission`), `User` (`UsersPermission`), `Audit Logs` (`AuditLogDetail`), `Notification Rule` (`CreateNotification`), `Branches` (`BranchSeed`), `Sales Orders` (`CreateTenantFromOrderDialog`), `Dynamic Services` (`DynamicServiceEditor` — hem toolbar hem satır) |
|
||||
| Yazmayı servise devretme | `Tenant`, `Role`, `User` — `*ServiceAddress = "list-form-dynamic-api/{tenant\|role\|user}-{insert\|update}"`; `Notification` — `notification-insert` (çoklu değer `MultiValueStringJsonConverter`) |
|
||||
| Ekle düğmesini diyaloğa bağlama | `Dynamic Services` — `EditingOptionJson.AddDialogName = "DynamicServiceEditor"` |
|
||||
|
|
|
|||
204
.github/instructions/lowcode.instructions.md
vendored
204
.github/instructions/lowcode.instructions.md
vendored
|
|
@ -251,6 +251,7 @@ bildirilir:
|
|||
| Konu | Neden sorulmaz |
|
||||
| --- | --- |
|
||||
| Tenant + audit kolonları (`Id`, `TenantId`, `CreationTime`, `CreatorId`, `LastModificationTime`, `LastModifierId`, `IsDeleted`, `DeletionTime`, `DeleterId`) | Yeni tabloda varsayılan; kullanıcı "audit yok" demedikçe eklenir |
|
||||
| Tanım tablosunda `IsActive` | Yeni **tanım** tablosunda varsayılan (aşağıda); kullanıcı "aktif/pasif yok" demedikçe eklenir |
|
||||
| Metinlerin EN + TR olması, dil anahtarı üzerinden verilmesi | Platform sözleşmesi |
|
||||
| İsimlendirme (`ListFormCode`, menü kodu, yetki adları, dosya adları) | §2'den türer |
|
||||
| Seed dosyası yazılıp yazılmayacağı | Her artefakt seed'lenir (§0.2) |
|
||||
|
|
@ -258,6 +259,7 @@ bildirilir:
|
|||
| Parametreli SQL kullanımı, yetki kontrolü, tenant izolasyonu | Tartışmaya açık değil |
|
||||
| Depoda okunabilen tablo/kolon/menü/yetki adları | Önce ara, bulduğunu **doğrulat**, sıfırdan sorma |
|
||||
| Var olan bir tanım tablosunun (ülke, para birimi, departman, ünvan, birim) yeniden üretilip üretilmeyeceği | Üretilmez; mevcut tabloya lookup bağlanır (reference §12.5) |
|
||||
| Liste ekranının üstündeki KPI kartları (`Widgets`) | List ekranı en az 3 kartla gelir, kartlar tablonun durum/bayrak/tarih/tutar kolonlarından seçilir (§4.6.1) |
|
||||
|
||||
Buna karşılık **her zaman sorulacak** üç şey vardır, çünkü yanlış varsayım geri dönülmez sonuç
|
||||
üretir: **hangi tabloya yazılacağı**, **kimin onaylayacağı** (onay süreci varsa) ve
|
||||
|
|
@ -503,11 +505,12 @@ ister ve kiracı kullanıcısına hiçbiri açılmaz.
|
|||
| `get_dev_guide` | Bu dosyanın (`lowcode`) ya da `reference`'ın başlık listesini / bir bölümünü döner | Bölüm numarayla istenir (`4.3`); aynı numarayı iki başlık taşımamalıdır |
|
||||
| `create_table` | Yeni SQL Server tablosu kurar, `sql/object/{Tablo}.sql` dosyasını yazar | Ek olarak `App.SqlQueryManager` ister; `Id`/`TenantId`/audit kolonlarını kendi ekler (`fullAudited`, `multiTenant` varsayılan açık); var olan tabloyu reddeder. View/SP/fonksiyon kurulamaz |
|
||||
| `list_wizards` · `get_wizard` | Wizard dosyalarını listeler (menü yolu, bağımlılar) · dosyayı diskteki hâliyle döner | Var olan menü kökünü/izin grubunu bulmak için önce `list_wizards` |
|
||||
| `deploy_wizard` | Wizard cevabından ekranın wizard dosyasını ve menü/yetki/dil seed satırlarını yazar; veritabanına yazmaz (§4.0) | Girdi §4.4'teki düz cevap ya da `get_wizard` çıktısıyla aynı dosya biçimi; enum'lar sayı. `replaceFileName` = `EditFileName` (§4.5). İstek demo veri taşıyorsa `SeedData: true` + `seedRows` (§0.10.1). Çıkıştaki `isPending: true` ekranın henüz kurulmadığını söyler — kullanıcı Wizard Manager'da DB Migrate çalıştırır; model migrate'i beklemez |
|
||||
| `deploy_wizard` | Wizard cevabından ekranın wizard dosyasını ve menü/yetki/dil seed satırlarını yazar; veritabanına yazmaz (§4.0) | Girdi §4.4'teki düz cevap ya da `get_wizard` çıktısıyla aynı dosya biçimi; enum'lar sayı. `replaceFileName` = `EditFileName` (§4.5). List ekranında `Widgets` dolu gelir — en az 3 kart, kiracı tablosunda `"TenantId" = '@TENANTID'` (§4.6.1). İstek demo veri taşıyorsa `SeedData: true` + `seedRows` (§0.10.1). Çıkıştaki `isPending: true` ekranın henüz kurulmadığını söyler — kullanıcı Wizard Manager'da DB Migrate çalıştırır; model migrate'i beklemez |
|
||||
| `delete_wizard` | Wizard'ı ve yalnızca onun ürettiği kayıtları siler; `dropTables: true` ekranın tablosunu da düşürür | Bağımlı wizard varsa reddeder. Tablo kullanıcı açıkça onaylamadan düşürülmez; `App.SqlQueryManager` ister (§4.5) |
|
||||
| `start_dev_request` | `App.DevRequests` talebini üstlenir: `Ready to Start` durumundaysa `In Progress` durumuna alır, değilse reddeder | Ek olarak `App.DevRequests.Update` ister. Açıklamadaki görseller ayrı resim içeriği olarak gelir (§0.11) |
|
||||
| `get_dev_request` | Talebi durumuna dokunmadan okur (aynı metin + resim içerikleri) | Yarım kalan talebe dönmek için; `App.DevRequests` ister |
|
||||
| `append_dev_request_note` | Talep açıklamasının sonuna HTML ekler | Sonuç notu için; `Description`'ı `update_row` ile yazma (§0.11) |
|
||||
| `get_dev_request` | Talebi durumuna dokunmadan okur (aynı metin + analiz + resim içerikleri) | Analizden önce ya da yarım kalan talebe dönmek için; `App.DevRequests` ister |
|
||||
| `save_dev_request_analysis` | `Backlog`/`Analysis` durumundaki talebin analizini `Analysis` kolonuna yazar (var olanı değiştirir), talebi `Analysis` durumuna alır; diğer durumları reddeder | Ek olarak `App.DevRequests.Update` ister. Standart §0.12; `Description`'a dokunmaz |
|
||||
| `append_dev_request_note` | Talebin `Analysis` kolonunun sonuna HTML ekler | Üretim sonuç notu için; `Description` ve `Analysis` `update_row` ile yazılmaz (§0.11) |
|
||||
| `add_dev_request_subtasks` | `SubTasks` listesine tamamlanmış satırlar ekler | Atanan = bağlanan kullanıcı, bitiş = bugün (sunucu yazar); `SubTasks`'ı `update_row` ile yazma (§0.11) |
|
||||
| `verify_wizard` | DB Migrate ile kurulmuş ekranı veri yazmadan doğrular (migrate bekleyen dosyada `install` kontrolü başarısız döner, diğerleri yapılmaz): menü, yetki tanımı, ekran sorgusu (List) ya da bileşen (Custom), seed kapanışı | İsteğe bağlı: kullanıcı migrate sonrası isterse. Talep döngüsünün (§0.11) adımı değildir. `passed` yalnızca engelleyici bir kontrol kalırsa `false` |
|
||||
| `list_components` · `get_component` · `save_component` · `delete_component` | Custom component okuma/yazma (ada göre upsert) | `save_component` visual designer bileşenini reddeder — yalnız Code modu (§7.1.1) |
|
||||
|
|
@ -554,10 +557,17 @@ talep girer (`App.DevRequest`). Döngüyü `/mcp`'ye bağlı bir **host** kullan
|
|||
kiracı bağlamında geliştirme araçları kapalıdır. Gereken yetkiler: `App.DevRequests.Update`,
|
||||
`App.DeveloperKit.McpServers.Invoke` + `.Write` ve üretimin kendi yetkileri (§0.10).
|
||||
|
||||
İki giriş vardır; ikisi de istemcide hazır prompt (komut) olarak görünür (`McpPromptNames`):
|
||||
Talep yaşam döngüsü: `Backlog` → **`Analysis`** (MCP analiz eder, §0.12) → `Ready to Start`
|
||||
(analizi beğenen kullanıcı taşır; beğenmezse analiz yeniden yazılır ya da elle düzeltilir) →
|
||||
`In Progress` → `Testing` → `Staging`. Üretim `Ready to Start`'tan başlar ve talebin
|
||||
`Description`'ı ile onaylanmış `Analysis`'i birlikte tarif sayılır.
|
||||
|
||||
Girişler istemcide hazır prompt (komut) olarak görünür (`McpPromptNames`):
|
||||
|
||||
| Prompt | Kapsam |
|
||||
| --- | --- |
|
||||
| `analyze_dev_request` (`request`: Id ya da tam başlık) | Yalnızca o talebi analiz eder (§0.12); hiçbir şey üretmez |
|
||||
| `analyze_backlog_requests` | `Backlog` durumundaki bütün talepler, `SortOrder` sırasıyla analiz edilir |
|
||||
| `process_dev_request` (`request`: Id ya da tam başlık) | Yalnızca o talep. Talep başlatılamazsa hiçbir şey üretilmez, başka talebe geçilmez |
|
||||
| `process_ready_requests` | `Ready to Start` durumundaki bütün talepler, `SortOrder` sırasıyla; başlatılamayan talep atlanır |
|
||||
|
||||
|
|
@ -565,6 +575,8 @@ Kullanıcı prompt yerine düz metin de yazabilir; aynı anlama gelir:
|
|||
|
||||
| Kullanıcı yazar | Karşılığı |
|
||||
| --- | --- |
|
||||
| `<Id ya da "başlık"> talebini analiz et` | `analyze_dev_request` — yalnızca o talep |
|
||||
| `Analiz et` · `Backlog'daki talepleri analiz et` | `analyze_backlog_requests` |
|
||||
| `<Id ya da "başlık"> talebini işle` | `process_dev_request` — yalnızca o talep |
|
||||
| `Ready to Start durumundaki talepleri sırayla işle` · `Ready olanları işle` | `process_ready_requests` |
|
||||
| `Ready to Start durumundaki talepleri Id'leriyle listele` | Yalnızca adım 1 (`query_list`); hiçbir şey üstlenilmez, üretilmez |
|
||||
|
|
@ -581,7 +593,7 @@ talebin metnini döndürür. Başlık birden fazla talebe uyuyorsa Id ister. `St
|
|||
| --- | --- | --- |
|
||||
| 1. Kuyruğu oku (yalnızca toplu) | `query_list` — `ListFormCode: App.DevRequests`, filtre `["Status","=","Ready to Start"]`, sıralama `SortOrder` | — |
|
||||
| 2. Talebi üstlen | `start_dev_request` — Id ya da tam başlık. Yarım kalmış talebe dönüşte `get_dev_request` | In Progress |
|
||||
| 3. Üret | §0.3 keşif (depodan/araçlardan; kullanıcıya soru yok) → `create_table` / `deploy_wizard` / `save_component`; talep demo veri taşıyorsa `SeedData: true` + `seedRows` (§0.10.1); bu dosyanın kuralları aynen geçerlidir | — |
|
||||
| 3. Üret | Analizin **Üretim planı** sırayla, aynen uygulanır (aşağıdaki kurallar) → `create_table` / `deploy_wizard` / `save_component`; demo veri varsa `SeedData: true` + `seedRows` (§0.10.1); bu dosyanın kuralları aynen geçerlidir | — |
|
||||
| 4. Teslim | Sonuç kaydı + `update_row` → `Testing`. DB Migrate beklenmez; talep burada biter | Testing |
|
||||
| 5. Test | Kullanıcı DB Migrate çalıştırır, ekranı elle test eder ve talebi kendisi `Staging`'e alır (geri dönüşte `In Progress`). MCP bu adımı yapmaz | Staging |
|
||||
|
||||
|
|
@ -590,9 +602,12 @@ talebin metnini döndürür. Başlık birden fazla talebe uyuyorsa Id ister. `St
|
|||
verilerek (`"deploy_wizard: Mrp.Orders.json"`). Araç satırı tamamlanmış yazar, atanan kişiyi
|
||||
bağlanan kullanıcı, bitiş tarihini bugün yapar ve var olan satırları korur. `SubTasks`
|
||||
`update_row` ile yazılmaz.
|
||||
- `Description` — `append_dev_request_note` ile sonuna `<h4>MCP</h4>` başlığıyla kısa özet:
|
||||
üretilenler ve DB Migrate bekleyen wizard'lar. `Description` hiçbir zaman `update_row`
|
||||
ile yazılmaz — base64 görselleri taşır ve yeniden yazmak onları siler.
|
||||
- `Analysis` — `append_dev_request_note` ile sonuna `<h4>MCP</h4>` başlığıyla kısa özet:
|
||||
tablo, ekran, alan, demo veri, menü/yetki, analize eklenen varsayılanlar ve DB Migrate bekleyen
|
||||
wizard'lar. Tekrar üretimde yeni bir `<h4>MCP</h4>` bloğu eklenir, eskisi silinmez.
|
||||
- `Description` **talebin yazıldığı hâliyle kalır**; MCP ona hiçbir şey yazmaz. `Description` ve
|
||||
`Analysis` hiçbir zaman `update_row` ile yazılmaz — açıklama base64 görselleri taşır ve yeniden
|
||||
yazmak onları siler.
|
||||
|
||||
**Görseller.** Talebe yapıştırılan ekran görüntüleri açıklamada base64 durur; araç onları metinden
|
||||
ayıklar ve ayrı resim içeriği olarak verir, açıklamada yerlerinde `[image-N]` kalır. Kullanıcıdan
|
||||
|
|
@ -600,12 +615,14 @@ görseli yeniden istemezsin. Görsel talebin parçasıdır: ekran düzenini (§0
|
|||
değildir) ve içindeki satırları taşır; satırlar demo veridir (§0.10.1).
|
||||
|
||||
**Kurallar**
|
||||
- Talep tarifin kendisidir: kullanıcıya soru sorulmaz, belirsiz noktada §0.6'nın önerilen
|
||||
varsayılanı seçilir ve sonuç notuna yazılır. Döngü DB Migrate'i beklemez.
|
||||
- Talep §0.6 keşif turunu cevaplayacak kadar net değilse üretme; `Backlog`'a geri al ve eksik
|
||||
soruları `Description` sonuna yaz. Tabloya yazma hedefi, onaycı ve hesap formülü talepten
|
||||
okunamıyorsa varsayılmaz.
|
||||
- Çerçeve dışı bir ihtiyaç (§0.8) çıkarsa talebi `In Progress`'te bırak ve kullanıcıya sor.
|
||||
- **Onaylanmış analiz tarifin kendisidir; üretim karar vermez.** Soru sorulmaz, seçim yapılmaz,
|
||||
analizin yazdığı hiçbir şey değiştirilmez ya da daraltılmaz. Bu dosyadaki "kullanıcıya sor"
|
||||
kuralları (onaycı, görselden okunamayan zorunlu alan, çerçeve dışı ihtiyaç) bu döngüde analizin
|
||||
**(onay)** kararlarıyla çözülmüştür. Analizde karşılığı olmayan küçük bir ayrıntı (§0.6
|
||||
varsayılanı olan türden) varsayılanla tamamlanır ve sonuç notuna yazılır.
|
||||
- **Üretim tek seferde biter:** `In Progress` → `Testing` tek geçiştir; talep durdurulmaz,
|
||||
`Analysis`'e geri alınmaz, kısmi ekran üretilmez. Üretilebilirlik analizde doğrulanmıştır
|
||||
(§0.12). Döngü DB Migrate'i beklemez.
|
||||
- Test için ekrana satır ekleme — SeedSync açık ekranda seed dosyasına iz bırakır. Talebin kendi demo verisi test satırı değildir, §0.10.1 ile eklenir.
|
||||
- MCP talebi `Staging`'e almaz: deploy yalnızca dosya yazar, ekran migrate'ten sonra vardır ve testi kullanıcı yapar.
|
||||
|
||||
|
|
@ -614,6 +631,77 @@ kullanıcısı Wizard Manager'daki DB Migrate'i (`/api/setup/migrate`, `Setup.Mi
|
|||
migrate ekranları, menü/yetki/dil satırlarını ve demo verisini kurar, Redis'i temizler ve bütün
|
||||
kiracıları dolaşır. Rapor bu adımı tek satırla belirtir.
|
||||
|
||||
### 0.12 Talep analizi — `Backlog` → `Analysis`
|
||||
|
||||
"Analiz et" dendiğinde `Backlog` talepleri üretimden önce analiz edilir; kullanıcı analizi okuyup
|
||||
beğenirse talebi `Ready to Start`'a kendisi taşır, beğenmezse analiz değişir (MCP'ye yeniden
|
||||
analiz ettirir ya da `Analysis` sekmesinde elle düzeltir). Analiz **üretim yapmaz**: tablo, wizard,
|
||||
bileşen, seed dosyası yazılmaz; yalnızca okuma araçları ve `save_dev_request_analysis` kullanılır.
|
||||
|
||||
| Adım | Araç | Durum |
|
||||
| --- | --- | --- |
|
||||
| 1. Kuyruğu oku (yalnızca toplu) | `query_list` — `ListFormCode: App.DevRequests`, filtre `["Status","=","Backlog"]`, sıralama `SortOrder` | — |
|
||||
| 2. Talebi oku | `get_dev_request` — metin + görseller | — |
|
||||
| 3. Keşif | `list_wizards` (menü kökleri, modüller, izin grupları), `list_lists` / `describe_list` / `search_schema` (var olan ekran, tablo, lookup kaynağı), `list_components`, `get_dev_guide` (§0.6, §0.7, §0.8, §1) | — |
|
||||
| 4. Yaz | `save_dev_request_analysis` — analizin tamamı, HTML | Analysis |
|
||||
|
||||
**Durum kuralı sunucudadır:** araç yalnızca `Backlog` ya da `Analysis` durumundaki talebi kabul eder;
|
||||
`Analysis`'teki talebe yeniden çağrılırsa analiz değiştirilir. `Ready to Start` ve sonrası
|
||||
reddedilir. `Description`'a hiçbir koşulda yazılmaz.
|
||||
|
||||
**Analiz standardı.** HTML, aşağıdaki başlıklar bu sırayla (`<h4>`), boş kalan başlık yazılmaz.
|
||||
Kısa ve karar odaklı yaz; talebi tekrar anlatma.
|
||||
|
||||
**Analiz kapalı uçludur ve üretilebilirdir.** Üretim analizi tek seferde, karar vermeden uygular
|
||||
(§0.11); bu yüzden:
|
||||
- Her konu tek bir kararla yazılır. Açık soru, seçenek listesi ("(a) … (b) …"), "istenir mi?",
|
||||
"gerekirse", "ileride" bırakılmaz.
|
||||
- Talebin açıkça yazdığı şey (menü yolu, alan, kapsam) **girdidir**: değiştirilmez, daraltılmaz,
|
||||
alternatifi sunulmaz.
|
||||
- Talepte olmayan bilgi için analiz karar verir (**Kararlar**, madde 8). Tabloya yazma hedefi,
|
||||
onaycı ve hesap formülü de boş bırakılmaz: somut karar **(onay)** etiketiyle yazılır; talebi
|
||||
`Ready to Start`'a alan kullanıcı onları onaylamış olur.
|
||||
- Planın her adımının mevcut araçlarla (`create_table`, `deploy_wizard`, `save_component` ve
|
||||
sözleşmeleri, §4.4) yapılabildiği keşifte doğrulanır (madde 10).
|
||||
|
||||
1. **Amaç** — talep neden istenmiş, kim kullanacak, hangi işi çözecek (bir-iki cümle).
|
||||
2. **Görsel analizi** — talepte görsel varsa her biri için (`[image-N]`): ekran türü (liste, form,
|
||||
kanban, grafik, public sayfa…), görünen alanlar ve tipleri, gruplar/sekmeler, butonlar/aksiyonlar,
|
||||
filtreler, görünen satırlar (demo veri adayı, §0.10.1). Görsel yoksa başlık yazılmaz.
|
||||
3. **Çözüm yolu** — karar sırasındaki adım (Wizard List / Form / Custom component / SQL + Custom
|
||||
Endpoint / Dynamic Service) ve neden.
|
||||
4. **Menü yeri** — ekranın menüde nerede duracağı, tek bakışta okunur biçimde. Önce tam yol tek
|
||||
satırda (`Kök › Ara › Ekran`), sonra kökten ekrana her seviye bir satır:
|
||||
|
||||
| Seviye | Kod | Ad (TR / EN) | İkon | Durum |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Kök | … | … / … | … | Var / Yeni |
|
||||
| Ara (varsa, her biri) | … | … / … | … | Var / Yeni |
|
||||
| Ekran | `ListFormCode` | … / … | … | Yeni |
|
||||
|
||||
Altında `ModuleId`, izin grubu ve route yazılır. Var olan seviyeler `list_wizards` ve menü
|
||||
seed'inden okunur, kodları oradaki gibi yazılır; sıra (`Order`) deploy'da hesaplanır, analize
|
||||
yazılmaz. Talep menü yolunu yazıyorsa (başlıkta ya da açıklamada) o yol girdidir ve seviye
|
||||
seviye aynen alınır.
|
||||
5. **Veri modeli** — tablo adı (onek kuralı §2), yeni mi var olan mı; kolonlar (ad, tip, zorunluluk,
|
||||
uzunluk), lookup kaynakları, kiracı + audit (standing default 1). Yeni her tablonun ilk satırı
|
||||
**Tanım tablosu: Evet / Hayır** kararıdır, talepteki dayanağıyla (§8.1); atlanmaz. Evet ise
|
||||
kolon tablosunda `IsActive` (bit, varsayılan 1, zorunlu) yer alır — talepte ya da görselde
|
||||
olmasa da — ve madde 7 (form alanı + Aktif/Pasif widget'ı) ile madde 9 (`create_table` kolonu,
|
||||
demo veride `IsActive: true`) bunu taşır.
|
||||
6. **Etkilenen kısımlar** — uygulamada dokunulacak her yer: yeni/değişen ekranlar, alt formlar,
|
||||
menü, yetkiler (`C/R/U/D` + varsa özel), dil anahtarları, widget'lar, iş akışı/bildirim, SEO (public
|
||||
yol varsa), var olan ekranlara etkiler.
|
||||
7. **Görünümler ve davranış** — Grid/Card/Pivot/Chart (standing default 12), widget kartları (§4.6.1),
|
||||
`EditorScript` / `EditorOptions` ihtiyaçları, hesaplar ve doğrulamalar.
|
||||
8. **Kararlar** — talepten okunamayıp analizde verilen her karar, tek satır gerekçesiyle (§0.6);
|
||||
varsayılamayan konular **(onay)** etiketi taşır. "Açık sorular" başlığı yoktur.
|
||||
9. **Üretim planı** — sıralı artefakt listesi (`create_table: …`, `deploy_wizard: ….json`,
|
||||
`save_component: …`); üretimdeki `add_dev_request_subtasks` başlıkları bununla aynı olur.
|
||||
10. **Üretime hazır** — tek satır: **Evet**, ya da **Hayır — eksik yetenek: …** (çerçeve dışı
|
||||
ihtiyaç, §0.8, adıyla). Talebi daraltan geçici çözüm önerilmez; "Hayır" taşıyan talep yetenek
|
||||
eklenene kadar `Ready to Start`'a alınmaz.
|
||||
|
||||
## 1. Hangi istek → hangi artefakt
|
||||
|
||||
| İstek | Üretilecek artefakt | Bölüm |
|
||||
|
|
@ -963,7 +1051,7 @@ atanmaz (admin dahil); ekranı görecek roller yetkiyi rol ekranından alır.
|
|||
| `TreeOptionDto` | `KeyExpr`, `ParentIdExpr`; opsiyonel `HasItemsExpr`, `RootValue`, `AutoExpandAll`, `RecursiveSelection` |
|
||||
| `GanttOptionDto` | `KeyExpr`, `ParentIdExpr`, `TitleExpr`, `StartExpr`, `EndExpr`; `ProgressExpr`, `ScaleType` (`auto`/`minutes`/`hours`/`sixHours`/`days`/`weeks`/`months`/`quarters`/`years`, varsayılan `weeks`), `Allow*` bayrakları |
|
||||
| `SchedulerOptionDto` | `TextExpr`, `StartDateExpr`, `EndDateExpr`; `AllDayExpr`, `RecurrenceRuleExpr`, `RecurrenceExceptionExpr`, `StartDayHour`, `EndDayHour`, `DefaultView` (`day`/`week`/`workWeek`/`month`/`timelineDay`/`timelineWeek`/`timelineMonth`/`agenda`, varsayılan `week`), `CellDuration`, `FirstDayOfWeek`, `Allow*` |
|
||||
| `TodoOptionDto` | `TitleExpr`, `StatusExpr`; `DescriptionExpr`, `DueDateExpr`, `TagExpr`, `AssigneeExpr`, `PriorityExpr`, `CompletedExpr`, `OrderExpr`, `StatusOrder` (virgülle ayrılmış kolon sırası), `AllowDragging` |
|
||||
| `TodoOptionDto` | `TitleExpr`, `StatusExpr`; `DescriptionExpr`, `AnalysisExpr`, `DueDateExpr`, `TagExpr`, `AssigneeExpr`, `PriorityExpr`, `CompletedExpr`, `OrderExpr`, `StatusOrder` (virgülle ayrılmış kolon sırası), `AllowDragging` |
|
||||
|
||||
### 4.3 `Groups` — alanlar ve düzenleme formu
|
||||
|
||||
|
|
@ -1408,7 +1496,15 @@ cevaba yazılmaz.
|
|||
]
|
||||
}
|
||||
],
|
||||
"SubForms": [], "Widgets": [],
|
||||
"SubForms": [],
|
||||
// List ekranı widget'la gelir (§4.6.1): kiracı tablosu → her kartta TenantId koşulu.
|
||||
"Widgets": [
|
||||
{
|
||||
"SqlQuery": "SELECT 'App.Widget.Total' AS \"Title\", CAST(COUNT(*) AS VARCHAR(32)) AS \"Value\", 'blue' AS \"Color\", 'App.Mrp.Orders' AS \"SubTitle\", 'FaShoppingCart' AS \"Icon\" FROM \"Mrp_T_Order\" WHERE \"TenantId\" = '@TENANTID' UNION ALL SELECT 'App.Widget.Today', CAST(COUNT(*) AS VARCHAR(32)), 'green', 'App.Mrp.Orders', 'FaCalendarDay' FROM \"Mrp_T_Order\" WHERE \"TenantId\" = '@TENANTID' AND CAST(\"CreationTime\" AS DATE) = CAST(CURRENT_TIMESTAMP AS DATE) UNION ALL SELECT 'App.Widget.TotalRevenue', CAST(COALESCE(SUM(\"Total\"), 0) AS VARCHAR(32)), 'purple', 'App.Mrp.Orders', 'FaMoneyBillWave' FROM \"Mrp_T_Order\" WHERE \"TenantId\" = '@TENANTID'",
|
||||
"Title": "Title", "Value": "Value", "Color": "Color", "Icon": "Icon", "SubTitle": "SubTitle",
|
||||
"ValueClassName": "text-3xl", "ColSpan": 4, "ColGap": 3, "ClassName": "mb-3", "IsActive": true
|
||||
}
|
||||
],
|
||||
"WorkflowDto": { "ApprovalUserFieldName": "", "ApprovalDateFieldName": "",
|
||||
"ApprovalStatusFieldName": "", "ApprovalDescriptionFieldName": "",
|
||||
"ApprovalIsFilterUserName": false, "ApprovalIsResetWorkflow": false, "Criteria": [] }
|
||||
|
|
@ -1591,7 +1687,7 @@ widget, sorgunun döndürdüğü **her satır için bir kart** çizer:
|
|||
```jsonc
|
||||
"Widgets": [
|
||||
{
|
||||
"SqlQuery": "SELECT 'App.Mrp.Widget.OpenOrders' AS Baslik, COUNT(*) AS Deger, 'blue' AS Renk, 'FcClock' AS Ikon FROM Mrp_T_Order WHERE IsDeleted = 'false' AND Status = 1",
|
||||
"SqlQuery": "SELECT 'App.Mrp.Widget.OpenOrders' AS Baslik, COUNT(*) AS Deger, 'blue' AS Renk, 'FcClock' AS Ikon FROM Mrp_T_Order WHERE TenantId = '@TENANTID' AND Status = 1",
|
||||
"Title": "Baslik", // sorgudaki kolon adı
|
||||
"Value": "Deger", // sorgudaki kolon adı
|
||||
"Color": "Renk",
|
||||
|
|
@ -1612,11 +1708,57 @@ widget, sorgunun döndürdüğü **her satır için bir kart** çizer:
|
|||
çevirir, karşılığı yoksa metni aynen basar. Sorguya düz metin değil anahtar yaz.
|
||||
- Tek sorguyla birden çok kart üretmek için `UNION ALL` kullan — üç durum için üç widget kaydı
|
||||
açmana gerek yok.
|
||||
- Widget sorgusu ekranın otomatik tenant/şube filtresine tabi değildir ve `@TENANTID`/`@USERID`
|
||||
gibi token'lar burada **çözülmez** (bağlanmamış parametre hatası verir). Kırılım gerekiyorsa
|
||||
kiracıya özel bir `DataSource` kullan ya da kırılımı view'ın içine göm.
|
||||
- Widget sorgusu ekranın otomatik tenant/şube filtresine tabi değildir; kırılımı sorgu kendisi
|
||||
yazar. Sorgu `DefaultFilter` ile aynı token çözümünden geçer (`DefaultValueHelper`):
|
||||
kiracı tablosunda `"TenantId" = '@TENANTID'` yaz — host'ta `IS NULL`'a çevrilir, liste ekranıyla
|
||||
aynı kırılımı verir. `@USERID`, `@DATE` vb. de çözülür.
|
||||
Ayrıntı: `lowcode-reference.instructions.md` §5.3.
|
||||
|
||||
#### 4.6.1 Otomatik widget — Wizard ve MCP ile üretilen her liste ekranı
|
||||
|
||||
Wizard ekranından ya da MCP `deploy_wizard` ile üretilen her **List** ekranı `Widgets` ile
|
||||
gelir; kullanıcı "widget/KPI" demese de, sorulmadan eklenir ve tarifte tek satırla bildirilir.
|
||||
İstek widget'ı açıkça dışarıda bırakırsa ya da ekran aşağıdaki istisnalardan biriyse eklenmez.
|
||||
|
||||
- **En az 3 kart**, tek widget kaydında `UNION ALL` ile. `ColSpan = 12 / kartSayısı`
|
||||
(3 kart → 4, 4 kart → 3, 6 kart → 2), `ColGap: 3`, `ClassName: "mb-3"`,
|
||||
`ValueClassName: "text-3xl"`, `IsActive: true`.
|
||||
- **Kartları tablonun anlamlı kolonlarından seç**, sırayla:
|
||||
1. Durum/statü kolonu (`Status`, `ApprovalStatus`, lookup'lı `dxSelectBox`) → değer başına bir
|
||||
kart; başlık lookup'taki dil anahtarı.
|
||||
2. Boolean bayraklar (`IsActive`, `IsPublished`, `IsPinned`, `IsSent`…) → Aktif/Pasif,
|
||||
Yayında/Taslak gibi çift.
|
||||
3. Tarih kolonu → "Bugün" (`CAST("Kolon" AS DATE) = CAST(CURRENT_TIMESTAMP AS DATE)`),
|
||||
"Yaklaşan" (`"Kolon" >= CURRENT_TIMESTAMP`), "Süresi dolmuş".
|
||||
4. Sayısal/tutar kolonu → `COALESCE(SUM("Kolon"), 0)` (Toplam tutar, toplam görüntülenme).
|
||||
5. Hiçbiri 3 kartı doldurmuyorsa `App.Widget.Total` ile başla.
|
||||
- **Sorgu kuralları:**
|
||||
- Tablo `TenantId` taşıyorsa (`IsTenant: true`) her karta `"TenantId" = '@TENANTID'` yazılır
|
||||
(§4.6, reference §5.3). Unutulursa ortak veritabanında kartlar bütün kiracıları sayar.
|
||||
- `IsDeleted` koşulu widget'a **yazılmaz**.
|
||||
- Değer `CAST(... AS VARCHAR(32))` ile metne çevrilir: sayı ve tutar kartları aynı
|
||||
`UNION ALL`'da tip çakışmasına düşmez.
|
||||
- Tanımlayıcılar çift tırnaklı yazılır, sağlayıcıya özgü fonksiyon (`DATEADD`, `GETDATE`,
|
||||
`NOW()`) kullanılmaz; sorgu hem SQL Server hem PostgreSQL'de çalışmalıdır.
|
||||
- **Metin:** `Title` bir dil anahtarıdır. Önce var olan anahtarlar kullanılır: genel
|
||||
`App.Widget.*` (`Total`, `Active`, `Passive`, `Published`, `Draft`, `Today`, `Upcoming`,
|
||||
`Pending`, `Sent`, `TotalViews`, `TotalRevenue`…), lookup'ın `App.StaticLookup.*` anahtarları,
|
||||
alanın `App.Listform.ListformField.*` başlığı. Karşılığı yoksa yeni anahtar EN + TR yazılır.
|
||||
`SubTitle` ekranın kendi başlık anahtarıdır (`ListFormCode`).
|
||||
- **Renk ve ikon:** `blue` toplam, `green` olumlu/aktif, `gray` pasif/taslak, `red` hata/iptal,
|
||||
`yellow`/`orange` bekleyen, `purple` tutar/özet. İkon `react-icons/fa` adıdır (`FaCheckCircle`,
|
||||
`FaTimesCircle`, `FaCalendarDay`, `FaEye`, `FaMoneyBillWave`…).
|
||||
- **İstisna — widget eklenmez:** Form ve Chart ekranları; alt form olarak kullanılan menüsüz
|
||||
ekranlar (`CreateMenu: false`, beğeni/yorum/seçenek gibi detay tabloları); `IsActive` de
|
||||
taşımayan, yalnızca kod + ad tutan eski tanım listeleri. Bunlarda anlamlı bir kırılım yoktur,
|
||||
şerit yalnızca kayıt sayısını tekrarlar. Yeni tanım tablosu `IsActive` ile gelir (§8.1) ve
|
||||
Toplam · Aktif · Pasif kartlarını alır.
|
||||
- Kartların sorgusunu seed dosyasına yazmadan önce tablo kolonları doğrulanır
|
||||
(MCP'de `search_schema`, ekranda tablo tasarımcısı). Var olmayan kolona yazılmış kart ekranı hatayla açar.
|
||||
|
||||
Seeder'da (`ListFormSeeder_*`) aynı kural `DefaultWidgetsJson(tablo, listFormName, tenant,
|
||||
params WidgetCard[])` ile uygulanır.
|
||||
|
||||
Alt form olarak kullanılacak ekranı **menüsüz wizard** (`CreateMenu: false`) ile üret; menüde
|
||||
görünmesin ama kendi yetkileri ve alan tanımları olsun. `Wizard.SeededAt` damgası ana
|
||||
wizard'ınkinden **küçük** olmalıdır (§0.2).
|
||||
|
|
@ -3065,6 +3207,26 @@ tabloları için yalnız `T`/`D` üretir.)
|
|||
Kullanıcı açıkça "tenant yok / audit yok" demedikçe Multi-Tenant + Full Audited setleri dahil
|
||||
edilir; her seferinde sorma.
|
||||
|
||||
**Tanım tablosu → `IsActive` otomatik.** Tanım tablosu, başka ekranların seçtiği kod/ad/parametre
|
||||
listesidir. Bir ekranın tanım olup olmadığına sabit bir ad listesiyle değil **talebin içeriğine
|
||||
bakılarak** karar verilir: talep ekranı tanımlamaların altına koyuyorsa (başlıktaki ya da
|
||||
açıklamadaki menü yolu, "tanım/parametre listesi" ifadesi) ya da amaç ekranın başka ekranlara
|
||||
seçim kaynağı olmasıysa ekran tanımdır. Karar analizde verilir ve gerekçesiyle yazılır; üretim
|
||||
kararı yeniden vermez. Tanım olan yeni tabloya iş kolonlarının sonuna, audit kolonlarından önce
|
||||
sorulmadan eklenir:
|
||||
|
||||
| Katman | `IsActive` |
|
||||
| --- | --- |
|
||||
| Tablo | `bit NOT NULL DEFAULT 1` (`create_table`'da iş kolonu olarak, `defaultValue: 1`) |
|
||||
| Liste | Görünür kolon, `dxCheckBox` |
|
||||
| Form | `dxCheckBox`, yeni kayıtta `true` (`InsertFieldsDefaultValueJson`) |
|
||||
| Widget | Toplam · Aktif · Pasif kartları (§4.6.1 madde 2) |
|
||||
| Demo veri | Satırlar `IsActive: true` ile yazılır |
|
||||
| Dil | `App.Listform.ListformField.IsActive` (var olan anahtar) |
|
||||
|
||||
Pasife alma silmenin yerine geçmez; silme açık kalır. Analiz bunu **Kararlar**'a değil **Veri
|
||||
modeli**'ne yazar (§0.12 madde 5); tanım tablosu olduğunu da orada belirtir. Var olan tabloya kolon eklenmez (şema değişikliği ayrı iştir).
|
||||
|
||||
### 8.2 View tasarımcısı (`SqlViewDesignerDialog`) — çalışma mantığı
|
||||
|
||||
Bir ekran birden fazla tablodan besleniyorsa, `SelectCommandType: 4` (Query) yerine **tasarlanmış
|
||||
|
|
@ -4169,6 +4331,8 @@ Prompt ile üretim yaptıysan ek olarak:
|
|||
- [ ] Liste ekranında `Grid`/`Card`/`Pivot`/`Chart` açık, `DefaultLayout: "grid"`; kapalı olan
|
||||
görünüm yalnızca istek onu açıkça dışarıda bıraktığı için kapalı (§9.8).
|
||||
- [ ] Açılan her görünümün zorunlu option alanı dolu (§9.8).
|
||||
- [ ] List ekranında en az 3 kartlık `Widgets` var (istisna değilse); kiracı tablosunda her kart
|
||||
`"TenantId" = '@TENANTID'` taşıyor, başlıklar dil anahtarı, kolonlar doğrulandı (§4.6.1).
|
||||
- [ ] Custom component'te route yetkisiz üretildiği için koruma bileşenin içinde
|
||||
(`designerPermission` / `*Permission` / `checkPermission`).
|
||||
- [ ] Custom component visual modda ise `Props.visualDesigner` ile `Code` marker'ı aynı dokümanı
|
||||
|
|
|
|||
22
README.md
22
README.md
|
|
@ -1410,14 +1410,16 @@ liste süzgeci ve çağrı kapısı aynı kuralı kullanır.
|
|||
| `delete_wizard` | Wizard Manager'daki silmeyle aynı: `InsertedRecords` + ekranın veri aynası, bağımlı wizard varsa reddedilir. `dropTables: true` ekranın tablosunu (yalnızca tasarımcı/`create_table` ile kurulmuş ve başka yerde kullanılmayan) CRUD endpoint'leri, `sql/object` ve `crud` dosyalarıyla düşürür; model bunu kullanıcı onayı olmadan göndermez (`WizardTableCleaner`, `lowcode.instructions.md` §4.5). |
|
||||
| `verify_wizard` | Deploy edilmiş ekranı veri yazmadan doğrular: menü kaydı ve URL'si, menünün istediği yetki tanımı (`AbpPermissions`), List yolunda ekran sorgusu (`GetSelectAsync`, tek satır, çağıranın yetkisiyle), Custom yolunda bileşen (etkin, rota = menü URL'si) ve seed kapanışı (`ExportAnalysis` uyarıları; engellemez). İsteğe bağlıdır, migrate sonrasında kullanılır; `App.DevRequests` döngüsünün adımı değildir — döngü `Testing`'de biter, kullanıcı elle test edip `Staging`'e alır (`lowcode.instructions.md` §0.11). |
|
||||
| `start_dev_request` | Geliştirme talebini Id ya da tam başlıkla üstlenir: yalnızca `Ready to Start` durumundakini `In Progress` durumuna alır, diğerlerini reddeder; talebin metnini döndürür. Açıklamaya yapıştırılmış base64 görseller (`data:image/png|jpeg|gif|webp`) metinden ayıklanır (`McpDevRequestImages`), yerlerinde `[image-N]` kalır ve her biri ayrı MCP resim içeriği (`ImageContentBlock`) olarak döner — model görseli resim olarak görür, görsel hiçbir yere yüklenmez. `.Write` + `App.DevRequests.Update` ister. |
|
||||
| `get_dev_request` | Talebi durumuna dokunmadan, `start_dev_request` ile aynı biçimde (metin + resim içerikleri) okur; yarım kalan talebe dönmek için. `App.DevRequests` ister. |
|
||||
| `get_dev_request` | Talebi durumuna dokunmadan, `start_dev_request` ile aynı biçimde (metin + analiz + resim içerikleri) okur; analizden önce ya da yarım kalan talebe dönmek için. `App.DevRequests` ister. |
|
||||
| `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` | Talep açıklamasının sonuna HTML ekler, var olan metne ve görsellere dokunmaz. Açıklama base64 görsel taşıdığı için `update_row` ile yeniden yazılmaz. `.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. |
|
||||
|
||||
**Prompt'lar** (`McpPromptNames`, `McpDevelopPrompts`). İstemcide komut olarak görünür; yalnızca
|
||||
metindir, yetki taşımaz. `process_dev_request` tek talebi, `process_ready_requests` bütün
|
||||
`Ready to Start` kuyruğunu işler. İkisi de adımları tekrar etmez, `get_dev_guide` ile
|
||||
`lowcode.instructions.md` §0.11'e yönlendirir.
|
||||
metindir, yetki taşımaz. `analyze_dev_request` tek talebi, `analyze_backlog_requests` bütün
|
||||
`Backlog` kuyruğunu analiz edip `Analysis` durumuna alır (§0.12); `process_dev_request` tek talebi,
|
||||
`process_ready_requests` bütün `Ready to Start` kuyruğunu işler (§0.11). Hiçbiri adımları tekrar
|
||||
etmez, `get_dev_guide` ile `lowcode.instructions.md` ilgili bölümüne yönlendirir.
|
||||
|
||||
**Geliştirme taleplerini çalıştırma.** Komutun adı istemcideki sunucu adına göre değişir: aşağıdaki
|
||||
örnekler `claude mcp add sozsoft ...` ile eklenmiş sunucu içindir (`sozsoft-local` ile eklediysen
|
||||
|
|
@ -1426,6 +1428,8 @@ değilse sunucu reddeder ve hiçbir şey üretilmez.
|
|||
|
||||
| Ne istiyorsun | Claude Code | Claude.ai / Claude Desktop | Düz metin (her istemci) |
|
||||
| --- | --- | --- | --- |
|
||||
| Tek talebi analiz et | `/mcp__sozsoft__analyze_dev_request <Id>` | `+` menüsü → `sozsoft` → `analyze_dev_request` | `<Id ya da "başlık"> talebini analiz et` |
|
||||
| Backlog'u analiz et | `/mcp__sozsoft__analyze_backlog_requests` | `+` menüsü → `sozsoft` → `analyze_backlog_requests` | `Backlog'daki talepleri analiz et` |
|
||||
| Tek talep | `/mcp__sozsoft__process_dev_request <Id>` | Mesaj kutusundaki `+` menüsü → `sozsoft` → `process_dev_request`, `request` alanına Id ya da tam başlık | `<Id ya da "başlık"> talebini işle` |
|
||||
| Bütün kuyruk | `/mcp__sozsoft__process_ready_requests` | `+` menüsü → `sozsoft` → `process_ready_requests` | `Ready to Start durumundaki talepleri sırayla işle` |
|
||||
| Önce kuyruğu görmek | — | — | `Ready to Start durumundaki talepleri Id'leriyle listele` |
|
||||
|
|
@ -1459,7 +1463,7 @@ yetkisi yalnızca kapıdır. Bir çağrı üç katmandan geçer ve her katman ay
|
|||
| --- | --- | --- | --- |
|
||||
| `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.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`, `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 |
|
||||
| `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ı)
|
||||
|
||||
|
|
@ -1481,6 +1485,7 @@ yetkisi yalnızca kapıdır. Bir çağrı üç katmandan geçer ve her katman ay
|
|||
| `delete_component` | — | `.Write` | `App.DeveloperKit.CustomComponents.Delete` |
|
||||
| `start_dev_request` | — | `.Write` | `App.DevRequests.Update` |
|
||||
| `get_dev_request` | — | — | `App.DevRequests` |
|
||||
| `save_dev_request_analysis` | — | `.Write` | `App.DevRequests.Update` |
|
||||
| `append_dev_request_note` | — | `.Write` | `App.DevRequests.Update` |
|
||||
| `add_dev_request_subtasks` | — | `.Write` | `App.DevRequests.Update` |
|
||||
|
||||
|
|
@ -1541,7 +1546,10 @@ kimlik doğrulamayı istemci MCP OAuth akışıyla kendisi yürütür:
|
|||
|
||||
Üç ayrıntı bilinçlidir:
|
||||
- Yayıncı (`issuer`) `AuthServer:Authority`'den sabitlenir; ters vekil arkasında istekten
|
||||
türetilirse `http://` olur ve istemci keşif belgesini reddeder.
|
||||
türetilirse `http://` olur ve istemci keşif belgesini reddeder. Korumalı kaynak belgesindeki
|
||||
`authorization_servers` değeri bu `issuer` ile birebir aynıdır (sonda `/` dahil,
|
||||
`McpOAuthOptions.AuthorizationServer`); RFC 8414 §3.3'ü uygulayan istemci (`mcp-remote`)
|
||||
farkta `IssuerMismatchError` ile durur.
|
||||
- MCP istemcisinin gönderdiği RFC 8707 `resource` parametresi yok sayılır
|
||||
(`McpOAuthServerConfiguration`): OpenIddict kayıtsız kaynağı reddeder ya da token kitlesini
|
||||
`/mcp` adresine daraltır, API ise `Platform` kitlesini doğrular.
|
||||
|
|
|
|||
|
|
@ -147,6 +147,12 @@ public class McpDevRequestDto
|
|||
/// <summary>Aciklamadaki gorseller, metinde gectikleri sirayla.</summary>
|
||||
public List<McpDevRequestImageDto> Images { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// HTML. Talebin analizi ve MCP sonuc notlari; <see cref="Description"/> talebin yazildigi
|
||||
/// haliyle kalir, uretim ikisine birlikte bakar.
|
||||
/// </summary>
|
||||
public string? Analysis { get; set; }
|
||||
|
||||
public string? Priority { get; set; }
|
||||
|
||||
public string? Tags { get; set; }
|
||||
|
|
|
|||
|
|
@ -38,8 +38,14 @@ public interface IMcpDevelopAppService : IApplicationService
|
|||
Task<McpDevRequestDto> GetDevRequestAsync(string request);
|
||||
|
||||
/// <summary>
|
||||
/// Aciklamanin sonuna HTML ekler. Aciklama gomulu gorsel tasidigi icin satir guncellemesiyle
|
||||
/// yeniden yazilamaz; ekleme sunucuda yapilir.
|
||||
/// Talebin analizini yazar (var olani degistirir) ve talebi Analysis durumuna alir; yalnizca
|
||||
/// Backlog ya da Analysis durumundaki talep analiz edilir.
|
||||
/// </summary>
|
||||
Task<McpDevRequestDto> SaveDevRequestAnalysisAsync(string request, string html);
|
||||
|
||||
/// <summary>
|
||||
/// Analizin sonuna HTML ekler. Aciklama talebin yazildigi haliyle kalir; uretim notlari
|
||||
/// analize yazilir.
|
||||
/// </summary>
|
||||
Task AppendDevRequestNoteAsync(string request, string html);
|
||||
|
||||
|
|
|
|||
|
|
@ -383,8 +383,37 @@ public class McpDevelopAppService(
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aciklama base64 gorsel tasir; model onu update_row ile yeniden yazarsa gorselleri geri
|
||||
/// gonderemez ve siler. Ekleme bu yuzden sunucuda, var olan metne dokunmadan yapilir.
|
||||
/// Durum kurali modelin dikkatine birakilmaz: yalnizca Backlog ya da Analysis durumundaki talep
|
||||
/// analiz edilir. Analysis'teki talebin analizi begenilmediyse yeniden yazilir; Ready to Start'a
|
||||
/// kullanici alir.
|
||||
/// </summary>
|
||||
public async Task<McpDevRequestDto> SaveDevRequestAnalysisAsync(string request, string html)
|
||||
{
|
||||
await EnsureToolAllowedAsync(McpToolNames.SaveDevRequestAnalysis);
|
||||
await AuthorizationService.CheckAsync(DevRequestUpdatePermission);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(html))
|
||||
{
|
||||
throw new UserFriendlyException(L[McpErrorCodes.DevRequestAnalysisRequired]);
|
||||
}
|
||||
|
||||
var devRequest = await FindDevRequestAsync(request);
|
||||
|
||||
if (devRequest.Status is not (McpConsts.DevRequestStatuses.Backlog or McpConsts.DevRequestStatuses.Analysis))
|
||||
{
|
||||
throw new UserFriendlyException(L[McpErrorCodes.DevRequestNotAnalyzable, devRequest.Title ?? devRequest.Id.ToString(), devRequest.Status ?? string.Empty]);
|
||||
}
|
||||
|
||||
devRequest.Analysis = html.Trim();
|
||||
devRequest.Status = McpConsts.DevRequestStatuses.Analysis;
|
||||
await devRequestRepository.UpdateAsync(devRequest, autoSave: true);
|
||||
|
||||
return ToDto(devRequest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aciklama talebin yazildigi haliyle kalir ve base64 gorsel tasir; uretim notlari analize
|
||||
/// eklenir. Ekleme sunucuda, var olan metne dokunmadan yapilir.
|
||||
/// </summary>
|
||||
public async Task AppendDevRequestNoteAsync(string request, string html)
|
||||
{
|
||||
|
|
@ -397,7 +426,7 @@ public class McpDevelopAppService(
|
|||
}
|
||||
|
||||
var devRequest = await FindDevRequestAsync(request);
|
||||
devRequest.Description = string.Concat(devRequest.Description, html.Trim());
|
||||
devRequest.Analysis = string.Concat(devRequest.Analysis, html.Trim());
|
||||
await devRequestRepository.UpdateAsync(devRequest, autoSave: true);
|
||||
}
|
||||
|
||||
|
|
@ -461,6 +490,7 @@ public class McpDevelopAppService(
|
|||
Status = devRequest.Status,
|
||||
Description = description,
|
||||
Images = images,
|
||||
Analysis = devRequest.Analysis,
|
||||
Priority = devRequest.Priority,
|
||||
Tags = devRequest.Tags,
|
||||
DueDate = devRequest.DueDate,
|
||||
|
|
|
|||
|
|
@ -103,6 +103,8 @@ public static class McpConsts
|
|||
/// </summary>
|
||||
public static class DevRequestStatuses
|
||||
{
|
||||
public const string Backlog = "Backlog";
|
||||
public const string Analysis = "Analysis";
|
||||
public const string ReadyToStart = "Ready to Start";
|
||||
public const string InProgress = "In Progress";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ public static class McpErrorCodes
|
|||
public const string DevRequestNotFound = Prefix + "DevRequestNotFound";
|
||||
public const string DevRequestAmbiguous = Prefix + "DevRequestAmbiguous";
|
||||
public const string DevRequestNotReady = Prefix + "DevRequestNotReady";
|
||||
public const string DevRequestNotAnalyzable = Prefix + "DevRequestNotAnalyzable";
|
||||
public const string DevRequestAnalysisRequired = Prefix + "DevRequestAnalysisRequired";
|
||||
public const string DevRequestNoteRequired = Prefix + "DevRequestNoteRequired";
|
||||
public const string DevRequestSubTasksRequired = Prefix + "DevRequestSubTasksRequired";
|
||||
public const string DevRequestSubTasksInvalid = Prefix + "DevRequestSubTasksInvalid";
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ namespace Sozsoft.Mcp;
|
|||
/// </summary>
|
||||
public static class McpPromptNames
|
||||
{
|
||||
public const string AnalyzeDevRequest = "analyze_dev_request";
|
||||
public const string AnalyzeBacklogRequests = "analyze_backlog_requests";
|
||||
public const string ProcessDevRequest = "process_dev_request";
|
||||
public const string ProcessReadyRequests = "process_ready_requests";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ public static class McpToolNames
|
|||
public const string VerifyWizard = "verify_wizard";
|
||||
public const string StartDevRequest = "start_dev_request";
|
||||
public const string GetDevRequest = "get_dev_request";
|
||||
public const string SaveDevRequestAnalysis = "save_dev_request_analysis";
|
||||
public const string AppendDevRequestNote = "append_dev_request_note";
|
||||
public const string AddDevRequestSubTasks = "add_dev_request_subtasks";
|
||||
public const string ListComponents = "list_components";
|
||||
|
|
@ -54,6 +55,7 @@ public static class McpToolNames
|
|||
VerifyWizard,
|
||||
StartDevRequest,
|
||||
GetDevRequest,
|
||||
SaveDevRequestAnalysis,
|
||||
AppendDevRequestNote,
|
||||
AddDevRequestSubTasks,
|
||||
ListComponents,
|
||||
|
|
@ -106,6 +108,7 @@ public static class McpToolNames
|
|||
[DeployWizard] = PlatformConsts.AppCodes.DeveloperKits.McpServers.Write,
|
||||
[DeleteWizard] = PlatformConsts.AppCodes.DeveloperKits.McpServers.Write,
|
||||
[StartDevRequest] = PlatformConsts.AppCodes.DeveloperKits.McpServers.Write,
|
||||
[SaveDevRequestAnalysis] = PlatformConsts.AppCodes.DeveloperKits.McpServers.Write,
|
||||
[AppendDevRequestNote] = PlatformConsts.AppCodes.DeveloperKits.McpServers.Write,
|
||||
[AddDevRequestSubTasks] = PlatformConsts.AppCodes.DeveloperKits.McpServers.Write,
|
||||
[SaveComponent] = PlatformConsts.AppCodes.DeveloperKits.McpServers.Write,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ModelContextProtocol.AspNetCore;
|
||||
using Sozsoft.Mcp.Tools;
|
||||
|
|
@ -25,7 +26,12 @@ public class McpHttpApiModule : AbpModule
|
|||
Configure<McpOAuthOptions>(options =>
|
||||
{
|
||||
options.BaseUrl = (configuration["App:SelfUrl"] ?? string.Empty).TrimEnd('/');
|
||||
options.AuthorizationServer = (configuration["AuthServer:Authority"] ?? string.Empty).TrimEnd('/');
|
||||
// OpenIddict keşif belgesindeki issuer'ı Uri.AbsoluteUri ile (sonda eğik çizgiyle) yazar;
|
||||
// RFC 8414 §3.3 gereği istemci iki değeri birebir karşılaştırır.
|
||||
var authority = configuration["AuthServer:Authority"];
|
||||
options.AuthorizationServer = string.IsNullOrWhiteSpace(authority)
|
||||
? string.Empty
|
||||
: new Uri(authority).AbsoluteUri;
|
||||
});
|
||||
|
||||
context.Services
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ public class McpOAuthOptions
|
|||
/// <summary>API'nin disaridan erisilen kok adresi, sonunda egik cizgi olmadan.</summary>
|
||||
public string BaseUrl { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Token'i veren yetkilendirme sunucusu (OpenIddict), sonunda egik cizgi olmadan.</summary>
|
||||
/// <summary>Token'i veren yetkilendirme sunucusu (OpenIddict); keşif belgesindeki issuer ile birebir aynı.</summary>
|
||||
public string AuthorizationServer { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Korumali kaynagin tanimlayicisi: istemcinin baglandigi /mcp adresi.</summary>
|
||||
|
|
|
|||
|
|
@ -23,9 +23,11 @@ internal static class McpServerInfoDefaults
|
|||
"section you need), inspect what exists with list_wizards/get_wizard and list_components/get_component, " +
|
||||
"then produce them with deploy_wizard and save_component; the guide's file-writing steps map to these tools. " +
|
||||
"Agree on screen type, data source, module and permissions with the user before deploying. " +
|
||||
"Analyzing development requests (Backlog -> Analysis) follows get_dev_guide section 0.12: read with get_dev_request, " +
|
||||
"write the analysis with save_dev_request_analysis, produce nothing and never touch Description; the user moves an approved analysis to Ready to Start. " +
|
||||
"Development requests (App.DevRequests) follow get_dev_guide section 0.11: claim each one with " +
|
||||
"start_dev_request, which only accepts requests in Ready to Start; images pasted into the request arrive as image content and are part of the request (layout and demo rows). " +
|
||||
"A request is its own brief: do not ask questions and do not wait for DB Migrate; deploy (demo rows go in deploy_wizard seedRows), move the request to Testing and finish. " +
|
||||
"A request (Description) and its approved Analysis are its own brief: do not ask questions and do not wait for DB Migrate; deploy (demo rows go in deploy_wizard seedRows), move the request to Testing and finish. " +
|
||||
"When a tool reports a missing permission, tell the user which permission is missing and stop; " +
|
||||
"do not try to reach the same result through another tool.";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,38 @@
|
|||
using System.ComponentModel;
|
||||
using System.ComponentModel;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace Sozsoft.Mcp.Tools;
|
||||
|
||||
/// <summary>
|
||||
/// Gelistirme dongusunun hazir prompt'lari. Proseduru tekrar etmez, rehberin 0.11 bolumune
|
||||
/// yonlendirir; boylece adimlar tek yerde (lowcode.instructions.md) degisir. Durum kurali
|
||||
/// start_dev_request'te sunucu tarafinda uygulanir.
|
||||
/// Gelistirme dongusunun hazir prompt'lari. Proseduru tekrar etmez, rehberin 0.11 (uretim) ve
|
||||
/// 0.12 (analiz) bolumlerine yonlendirir; boylece adimlar tek yerde (lowcode.instructions.md)
|
||||
/// degisir. Durum kurallari start_dev_request ve save_dev_request_analysis'te sunucu tarafinda
|
||||
/// uygulanir.
|
||||
/// </summary>
|
||||
[McpServerPromptType]
|
||||
public static class McpDevelopPrompts
|
||||
{
|
||||
[McpServerPrompt(Name = McpPromptNames.AnalyzeDevRequest)]
|
||||
[Description("Tek bir gelistirme talebini analiz eder ve Analysis durumuna alir. Talep Backlog ya da Analysis durumunda olmalidir.")]
|
||||
public static string AnalyzeDevRequest(
|
||||
[Description("Talebin Id'si (GUID) ya da tam basligi.")]
|
||||
string request)
|
||||
=> $"App.DevRequests kuyrugundaki '{request}' talebini analiz et; yalnizca bu talebi. " +
|
||||
"Once get_dev_guide ile lowcode 0.12 bolumunu oku ve oradaki standarda uy. " +
|
||||
"Talebi ve gorsellerini get_dev_request ile oku; analizi save_dev_request_analysis ile yaz. " +
|
||||
"Hicbir sey uretme (tablo, wizard, bilesen yok) ve Description'a dokunma. " +
|
||||
"Arac reddederse (talep Backlog ya da Analysis degil, bulunamadi ya da birden fazla eslesme var) nedenini bildir ve dur. " +
|
||||
"Sonunda kisa rapor ver: menu yeri, uretilecek artefaktlar, (onay) etiketli kararlar ve Uretime hazir satiri; talebi Ready to Start'a kullanicinin alacagini belirt.";
|
||||
|
||||
[McpServerPrompt(Name = McpPromptNames.AnalyzeBacklogRequests)]
|
||||
[Description("Backlog durumundaki tum gelistirme taleplerini SortOrder sirasiyla analiz eder ve Analysis durumuna alir.")]
|
||||
public static string AnalyzeBacklogRequests()
|
||||
=> "App.DevRequests kuyrugundaki Backlog taleplerini query_list ile SortOrder sirasiyla oku ve tek tek analiz et. " +
|
||||
"Once get_dev_guide ile lowcode 0.12 bolumunu oku ve oradaki standarda uy. " +
|
||||
"Her talebi get_dev_request ile oku, analizi save_dev_request_analysis ile yaz; reddedilen talebi atla ve rapora yaz. " +
|
||||
"Hicbir sey uretme ve Description'a dokunma. " +
|
||||
"Sonunda parti raporu ver: talep basina menu yeri, (onay) etiketli karar sayisi ve Uretime hazir durumu; talepleri Ready to Start'a kullanicinin alacagini tek satirda belirt.";
|
||||
|
||||
[McpServerPrompt(Name = McpPromptNames.ProcessDevRequest)]
|
||||
[Description("Tek bir gelistirme talebini isler. Talep Ready to Start durumunda olmalidir.")]
|
||||
public static string ProcessDevRequest(
|
||||
|
|
@ -20,7 +42,7 @@ public static class McpDevelopPrompts
|
|||
"Once get_dev_guide ile lowcode 0.11 bolumunu oku ve oradaki adimlari uygula. " +
|
||||
"Talebi start_dev_request ile ustlen. Arac reddederse (talep Ready to Start degil, bulunamadi ya da " +
|
||||
"birden fazla eslesme var) hicbir sey uretme, baska bir talebe gecme: nedenini bildir ve dur. " +
|
||||
"Talep tarifin kendisidir: soru sorma, DB Migrate'i bekleme; talebi Testing'e alip bitir. " +
|
||||
"Talep (Description) ve onaylanmis analizi (Analysis) birlikte tarifin kendisidir: soru sorma, DB Migrate'i bekleme; talebi Testing'e alip bitir. " +
|
||||
"Sonunda rapor ver: uretilen artefaktlar, talebin son durumu ve DB Migrate gerektigi.";
|
||||
|
||||
[McpServerPrompt(Name = McpPromptNames.ProcessReadyRequests)]
|
||||
|
|
@ -29,6 +51,6 @@ public static class McpDevelopPrompts
|
|||
=> "App.DevRequests kuyrugundaki Ready to Start taleplerini query_list ile SortOrder sirasiyla oku ve tek tek isle. " +
|
||||
"Once get_dev_guide ile lowcode 0.11 bolumunu oku ve oradaki adimlari uygula. " +
|
||||
"Her talebi start_dev_request ile ustlen; reddedilen talebi atla ve rapora yaz. " +
|
||||
"Talep tarifin kendisidir: soru sorma, DB Migrate'i bekleme; her talebi Testing'e alip digerine gec. " +
|
||||
"Talep (Description) ve onaylanmis analizi (Analysis) birlikte tarifin kendisidir: soru sorma, DB Migrate'i bekleme; her talebi Testing'e alip digerine gec. " +
|
||||
"Sonunda parti raporu ver: talep basina uretilen artefaktlar ve son durum; DB Migrate gerektigini tek satirda belirt.";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,21 +91,30 @@ public sealed class McpDevelopTools(IMcpDevelopAppService developAppService)
|
|||
=> await developAppService.VerifyWizardAsync(fileName);
|
||||
|
||||
[McpServerTool(Name = McpToolNames.StartDevRequest, Destructive = false, Idempotent = false)]
|
||||
[Description("App.DevRequests kuyrugundaki bir gelistirme talebini ustlenir: talep Ready to Start durumundaysa In Progress'e alir ve talebin metnini (baslik, HTML aciklama, oncelik, etiketler, SubTasks) dondurur; baska bir durumdaysa reddeder. Aciklamaya yapistirilmis gorseller ayri resim icerigi olarak gelir, aciklamada yerlerinde [image-N] isareti durur; gorseldeki ekran duzeni ve satirlar talebin parcasidir (demo veri icin deploy_wizard SeedData). Bir talep uzerinde calismaya bu aracla baslanir, Status'u update_row ile In Progress yapma. Sonraki adimlar icin get_dev_guide lowcode 0.11.")]
|
||||
[Description("App.DevRequests kuyrugundaki bir gelistirme talebini ustlenir: talep Ready to Start durumundaysa In Progress'e alir ve talebin metnini (baslik, HTML aciklama, onaylanmis HTML analiz, oncelik, etiketler, SubTasks) dondurur; baska bir durumdaysa reddeder. Aciklamaya yapistirilmis gorseller ayri resim icerigi olarak gelir, aciklamada yerlerinde [image-N] isareti durur; gorseldeki ekran duzeni ve satirlar talebin parcasidir (demo veri icin deploy_wizard SeedData). Uretim aciklama ile analize birlikte dayanir; analizdeki menu, tablo ve kapsam kararlari onaylanmistir. Bir talep uzerinde calismaya bu aracla baslanir, Status'u update_row ile In Progress yapma. Sonraki adimlar icin get_dev_guide lowcode 0.11.")]
|
||||
public async Task<IList<ContentBlock>> StartDevRequestAsync(
|
||||
[Description("Talebin Id'si (GUID) ya da tam basligi. Ayni basligi tasiyan birden fazla talep varsa Id ver.")]
|
||||
string request)
|
||||
=> ToContent(await developAppService.StartDevRequestAsync(request));
|
||||
|
||||
[McpServerTool(Name = McpToolNames.GetDevRequest, ReadOnly = true)]
|
||||
[Description("Bir gelistirme talebini durumuna dokunmadan okur; start_dev_request ile ayni cevabi (aciklama + ayri resim icerikleri) dondurur. Yarim kalmis (In Progress/Testing) bir talebe donerken ya da gorselleri yeniden gormek icin kullan; talebi ustlenmek icin degil.")]
|
||||
[Description("Bir gelistirme talebini durumuna dokunmadan okur; start_dev_request ile ayni cevabi (aciklama + ayri resim icerikleri) dondurur. Analiz oncesi talebi okumak, yarim kalmis (In Progress/Testing) bir talebe donmek ya da gorselleri yeniden gormek icin kullan; talebi ustlenmek icin degil.")]
|
||||
public async Task<IList<ContentBlock>> GetDevRequestAsync(
|
||||
[Description("Talebin Id'si (GUID) ya da tam basligi.")]
|
||||
string request)
|
||||
=> ToContent(await developAppService.GetDevRequestAsync(request));
|
||||
|
||||
[McpServerTool(Name = McpToolNames.SaveDevRequestAnalysis, Destructive = true, Idempotent = true)]
|
||||
[Description("Backlog ya da Analysis durumundaki bir talebin analizini (HTML) Analysis alanina yazar ve talebi Analysis durumuna alir; var olan analiz degistirilir, Description'a dokunulmaz. Diger durumlarda reddeder. Once get_dev_request ile talebi ve gorsellerini oku; analizin bolumleri ve standardi get_dev_guide lowcode 0.12'dedir. Analizi begenen kullanici talebi Ready to Start'a kendisi alir; MCP almaz.")]
|
||||
public async Task<IList<ContentBlock>> SaveDevRequestAnalysisAsync(
|
||||
[Description("Talebin Id'si (GUID) ya da tam basligi.")]
|
||||
string request,
|
||||
[Description("Analizin tamami, HTML.")]
|
||||
string html)
|
||||
=> ToContent(await developAppService.SaveDevRequestAnalysisAsync(request, html));
|
||||
|
||||
[McpServerTool(Name = McpToolNames.AppendDevRequestNote, Destructive = false, Idempotent = false)]
|
||||
[Description("Talebin HTML aciklamasinin sonuna ekleme yapar; var olan metne ve gorsellere dokunmaz. Aciklama gomulu gorsel tasidigi icin Description'i update_row ile yeniden yazma, sonuc notunu (<h4>MCP</h4> + uretilenler ve DB Migrate bekleyen wizard'lar) bu aracla ekle.")]
|
||||
[Description("Talebin Analysis alaninin sonuna HTML ekler; var olan analize ve Description'a dokunmaz. Description talebin yazildigi haliyle kalir; uretim sonuc notunu (<h4>MCP</h4> + uretilenler, varsayimlar ve DB Migrate bekleyen wizard'lar) bu aracla ekle. Description'i ya da Analysis'i update_row ile yazma.")]
|
||||
public async Task<string> AppendDevRequestNoteAsync(
|
||||
[Description("Talebin Id'si (GUID) ya da tam basligi.")]
|
||||
string request,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ public class TodoOptionDto
|
|||
public string TitleExpr { get; set; }
|
||||
public string StatusExpr { get; set; }
|
||||
public string DescriptionExpr { get; set; }
|
||||
public string AnalysisExpr { get; set; }
|
||||
public string DueDateExpr { get; set; }
|
||||
public string TagExpr { get; set; }
|
||||
public string AssigneeExpr { get; set; }
|
||||
|
|
|
|||
|
|
@ -252,7 +252,10 @@ public class ListFormSelectAppService : PlatformAppService, IListFormSelectAppSe
|
|||
Items = []
|
||||
};
|
||||
|
||||
var items = await dynamicDataRepository.QueryAsync(widget.SqlQuery, connectionString);
|
||||
// Widget sorgusu ekranın otomatik tenant filtresinden geçmez; kırılımı DefaultFilter ile
|
||||
// aynı token'larla (ör. "TenantId" = '@TENANTID', host'ta IS NULL) sorgu kendisi yazar.
|
||||
var sql = defaultValueHelper.GetDefaultValue(widget.SqlQuery);
|
||||
var items = await dynamicDataRepository.QueryAsync(sql, connectionString);
|
||||
if (items != null)
|
||||
{
|
||||
foreach (var item in items)
|
||||
|
|
|
|||
|
|
@ -962,54 +962,10 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
new() { FieldName = "UserType", FieldDbType = DbType.Int32, Value = "0", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||
new() { FieldName = "IsActive", FieldDbType = DbType.Boolean, Value = "true", CustomValueType = FieldCustomValueTypeEnum.Value }
|
||||
}),
|
||||
WidgetsJson = JsonSerializer.Serialize(new List<WidgetEditDto>
|
||||
{
|
||||
new()
|
||||
{
|
||||
ColGap = 3,
|
||||
ColSpan = 4,
|
||||
SqlQuery = @"
|
||||
SELECT
|
||||
N'Aktif' AS ""Title"",
|
||||
COUNT(""Id"") AS ""Value"",
|
||||
'blue' AS ""Color"",
|
||||
N'Aktif Kullanıcılar' AS ""SubTitle"",
|
||||
'FaUserCheck' AS ""Icon""
|
||||
FROM ""AbpUsers""
|
||||
WHERE ""IsActive"" = 'true'
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
N'Pasif' AS ""Title"",
|
||||
COUNT(""Id"") AS ""Value"",
|
||||
'green' AS ""Color"",
|
||||
N'Pasif Kullanıcılar' AS ""SubTitle"",
|
||||
'FaUserSlash' AS ""Icon""
|
||||
FROM ""AbpUsers""
|
||||
WHERE ""IsActive"" = 'false'
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
N'Doğrulama' AS ""Title"",
|
||||
COUNT(""Id"") AS ""Value"",
|
||||
'purple' AS ""Color"",
|
||||
N'Doğrulama bekleyenler' AS ""SubTitle"",
|
||||
'FaUserClock' AS ""Icon""
|
||||
FROM ""AbpUsers""
|
||||
WHERE ""IsVerified"" = 'false';
|
||||
",
|
||||
Title = "Title",
|
||||
Value = "Value",
|
||||
ValueClassName = "bg-5 text-sm",
|
||||
Color = "Color",
|
||||
Icon = "Icon",
|
||||
SubTitle = "SubTitle",
|
||||
ClassName = "mb-3",
|
||||
IsActive = true
|
||||
}
|
||||
})
|
||||
WidgetsJson = DefaultWidgetsJson("AbpUsers", listFormName, true,
|
||||
new("App.Widget.Active", "green", "FaUserCheck", "\"IsActive\" = 'true'"),
|
||||
new("App.Widget.Passive", "gray", "FaUserSlash", "\"IsActive\" = 'false'"),
|
||||
new("App.Widget.Unverified", "purple", "FaUserClock", "\"IsVerified\" = 'false'"))
|
||||
});
|
||||
|
||||
#region Users Fields
|
||||
|
|
@ -1397,6 +1353,10 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = "AbpAuditLogs",
|
||||
WidgetsJson = DefaultWidgetsJson("AbpAuditLogs", listFormName, true,
|
||||
new("App.Widget.Total", "blue", "FaHistory"),
|
||||
new("App.Widget.Today", "green", "FaCalendarDay", "CAST(\"ExecutionTime\" AS DATE) = CAST(CURRENT_TIMESTAMP AS DATE)"),
|
||||
new("App.Widget.Errors", "red", "FaExclamationTriangle", "(\"HttpStatusCode\" >= 400 OR \"Exceptions\" IS NOT NULL)")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
SortMode = GridOptions.SortModeSingle,
|
||||
|
|
@ -2511,6 +2471,10 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = "AbpSessions",
|
||||
WidgetsJson = DefaultWidgetsJson("AbpSessions", listFormName, true,
|
||||
new("App.Widget.Total", "blue", "FaDesktop"),
|
||||
new("App.Widget.SignedInToday", "green", "FaSignInAlt", "CAST(\"SignedIn\" AS DATE) = CAST(CURRENT_TIMESTAMP AS DATE)"),
|
||||
new("App.Widget.ActiveToday", "purple", "FaUserClock", "CAST(\"LastAccessed\" AS DATE) = CAST(CURRENT_TIMESTAMP AS DATE)")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
SortMode = GridOptions.SortModeSingle,
|
||||
|
|
@ -2982,6 +2946,11 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Announcement)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Announcement)), listFormName, true,
|
||||
new("App.Widget.Active", "green", "FaBullhorn", "(\"ExpiryDate\" IS NULL OR \"ExpiryDate\" >= CURRENT_TIMESTAMP)"),
|
||||
new("App.Widget.Pinned", "purple", "FaThumbtack", "\"IsPinned\" = 'true'"),
|
||||
new("App.Widget.Expired", "gray", "FaCalendarTimes", "\"ExpiryDate\" < CURRENT_TIMESTAMP"),
|
||||
new("App.Widget.TotalViews", "blue", "FaEye", Value: "COALESCE(SUM(\"ViewCount\"), 0)")),
|
||||
SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName),
|
||||
SeedSyncInsert = false,
|
||||
SeedSyncUpdate = false,
|
||||
|
|
@ -3537,6 +3506,10 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Survey)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Survey)), listFormName, true,
|
||||
new("App.Widget.Active", "green", "FaCheckCircle", "\"Status\" = 'active'"),
|
||||
new("App.Widget.Passive", "gray", "FaTimesCircle", "\"Status\" = 'passive'"),
|
||||
new("App.Widget.TotalResponses", "purple", "FaPollH", Value: "COALESCE(SUM(\"Responses\"), 0)")),
|
||||
SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName),
|
||||
SeedSyncInsert = false,
|
||||
SeedSyncUpdate = false,
|
||||
|
|
@ -4379,6 +4352,10 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.SocialPost)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SocialPost)), listFormName, true,
|
||||
new("App.Widget.Total", "blue", "FaComments"),
|
||||
new("App.Widget.Today", "green", "FaCalendarDay", "CAST(\"CreationTime\" AS DATE) = CAST(CURRENT_TIMESTAMP AS DATE)"),
|
||||
new("App.Widget.TotalLikes", "pink", "FaHeart", Value: "COALESCE(SUM(\"LikeCount\"), 0)")),
|
||||
SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName),
|
||||
SeedSyncInsert = false,
|
||||
SeedSyncUpdate = false,
|
||||
|
|
@ -5426,6 +5403,11 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Event)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Event)), listFormName, true,
|
||||
new("App.Widget.Upcoming", "blue", "FaCalendarAlt", "\"Date\" >= CURRENT_TIMESTAMP"),
|
||||
new("App.StaticLookup.Published", "green", "FaCheckCircle", "\"Status\" = 'published'"),
|
||||
new("App.StaticLookup.Draft", "gray", "FaPencilAlt", "\"Status\" = 'draft'"),
|
||||
new("App.StaticLookup.Cancelled", "red", "FaBan", "\"Status\" = 'cancelled'")),
|
||||
SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName),
|
||||
SeedSyncInsert = false,
|
||||
SeedSyncUpdate = false,
|
||||
|
|
@ -6714,9 +6696,9 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
ValueExpr = "key",
|
||||
LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] {
|
||||
new () { Key="Backlog", Name="App.StaticLookup.Backlog" },
|
||||
new () { Key="Analysis", Name="App.StaticLookup.Analysis" },
|
||||
new () { Key="Ready to Start", Name="App.StaticLookup.ReadyToStart" },
|
||||
new () { Key="In Progress", Name="App.StaticLookup.InProgress" },
|
||||
new () { Key="Code Review", Name="App.StaticLookup.CodeReview" },
|
||||
new () { Key="Testing", Name="App.StaticLookup.Testing" },
|
||||
new () { Key="Staging", Name="App.StaticLookup.Staging" }
|
||||
}),
|
||||
|
|
@ -6766,7 +6748,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
CaptionName = "App.Listform.ListformField.Description",
|
||||
Width = 0,
|
||||
ListOrderNo = 6,
|
||||
Visible = true,
|
||||
Visible = false,
|
||||
IsActive = true,
|
||||
AllowSearch = true,
|
||||
ValidationRuleJson = DefaultValidationRuleRequiredJson,
|
||||
|
|
@ -6775,6 +6757,26 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
EditorOptions = EditorOptions.Html()
|
||||
},
|
||||
new() {
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.String,
|
||||
FieldName = "Analysis",
|
||||
EditGroupOrderNo = 1,
|
||||
EditOrderNo = 7,
|
||||
EditorType2 = EditorTypes.dxHtmlEditor,
|
||||
ColSpan = 2,
|
||||
CaptionName = "App.Listform.ListformField.Analysis",
|
||||
Width = 0,
|
||||
ListOrderNo = 13,
|
||||
Visible = false,
|
||||
IsActive = true,
|
||||
AllowSearch = true,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
EditorOptions = EditorOptions.Html()
|
||||
},
|
||||
|
||||
new() {
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
|
|
@ -7209,6 +7211,10 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.NotificationRule)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.NotificationRule)), listFormName, true,
|
||||
new("App.Widget.Total", "blue", "FaSlidersH"),
|
||||
new("App.Widget.Active", "green", "FaCheckCircle", "\"IsActive\" = 'true'"),
|
||||
new("App.Widget.Passive", "gray", "FaTimesCircle", "\"IsActive\" = 'false'")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName),
|
||||
|
|
@ -7523,6 +7529,11 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Notification)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Notification)), listFormName, true,
|
||||
new("App.Widget.Sent", "green", "FaPaperPlane", "\"IsSent\" = 'true'"),
|
||||
new("App.Widget.Pending", "yellow", "FaHourglassHalf", "\"IsSent\" = 'false'"),
|
||||
new("App.Platform.Read", "blue", "FaEnvelopeOpen", "\"IsRead\" = 'true'"),
|
||||
new("App.Widget.Unread", "orange", "FaEnvelope", "\"IsSent\" = 'true' AND \"IsRead\" = 'false'")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
DefaultFilter = DefaultFilterJson,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Sozsoft.Platform.Enums;
|
||||
|
|
@ -133,6 +135,7 @@ public static class ListFormSeeder_DefaultJsons
|
|||
string TitleExpr = "Title",
|
||||
string StatusExpr = "Status",
|
||||
string DescriptionExpr = "Description",
|
||||
string AnalysisExpr = "Analysis",
|
||||
string DueDateExpr = "DueDate",
|
||||
string TagExpr = "Tags",
|
||||
string AssigneeExpr = "Assignees",
|
||||
|
|
@ -141,13 +144,14 @@ public static class ListFormSeeder_DefaultJsons
|
|||
string OrderExpr = "SortOrder",
|
||||
string TimesheetExpr = "Timesheets",
|
||||
string SubTaskExpr = "SubTasks",
|
||||
string StatusOrder = "Backlog, Ready to Start, In Progress, Code Review, Testing, Staging",
|
||||
string StatusOrder = "Backlog, Analysis, Ready to Start, In Progress, Testing, Staging",
|
||||
bool AllowDragging = true
|
||||
) => JsonSerializer.Serialize(new TodoOptionDto
|
||||
{
|
||||
TitleExpr = TitleExpr,
|
||||
StatusExpr = StatusExpr,
|
||||
DescriptionExpr = DescriptionExpr,
|
||||
AnalysisExpr = AnalysisExpr,
|
||||
DueDateExpr = DueDateExpr,
|
||||
TagExpr = TagExpr,
|
||||
AssigneeExpr = AssigneeExpr,
|
||||
|
|
@ -281,6 +285,53 @@ public static class ListFormSeeder_DefaultJsons
|
|||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ekranın üstündeki KPI şeridi: her kart tabloyu kendi koşuluyla sayar ve kartlar tek sorguda
|
||||
/// <c>UNION ALL</c> ile döner. Alt başlık ekranın adıdır; kartın başlığı dil anahtarıdır.
|
||||
/// Widget sorgusu ekranın otomatik tenant filtresinden geçmez; kiracı tablosunda
|
||||
/// <paramref name="tenant"/> verilmezse kartlar bütün kiracıları sayar.
|
||||
/// </summary>
|
||||
public static string DefaultWidgetsJson(string tableName, string subTitle, bool tenant, params WidgetCard[] cards)
|
||||
{
|
||||
var sql = string.Join("\nUNION ALL\n", cards.Select(card =>
|
||||
{
|
||||
var conditions = new[]
|
||||
{
|
||||
// Host'ta DefaultValueHelper bunu "IS NULL"a çevirir; liste ekranıyla aynı kırılım.
|
||||
tenant ? $"\"TenantId\" = '{DefaultValues.TenantId}'" : null,
|
||||
card.Where,
|
||||
}
|
||||
.Where(c => !string.IsNullOrEmpty(c))
|
||||
.ToArray();
|
||||
var where = conditions.Length > 0 ? $" WHERE {string.Join(" AND ", conditions)}" : string.Empty;
|
||||
|
||||
// Sayı ve tutar kartları aynı UNION'da buluşabilir; tip çakışmasın diye değer metne çevrilir.
|
||||
return $"SELECT '{card.Title}' AS \"Title\", CAST({card.Value} AS VARCHAR(32)) AS \"Value\", '{card.Color}' AS \"Color\", " +
|
||||
$"'{subTitle}' AS \"SubTitle\", '{card.Icon}' AS \"Icon\" FROM \"{tableName}\"{where}";
|
||||
}));
|
||||
|
||||
return DefaultWidgetsJson(sql, cards.Length);
|
||||
}
|
||||
|
||||
/// <summary>Kart sayısı sorgudan belli olmayan (gruplamalı) şeritler için; şerit 12 kolonu eşit böler.</summary>
|
||||
public static string DefaultWidgetsJson(string sqlQuery, int cardCount) => JsonSerializer.Serialize(new List<WidgetEditDto>
|
||||
{
|
||||
new()
|
||||
{
|
||||
ColGap = 3,
|
||||
ColSpan = Math.Max(2, 12 / cardCount),
|
||||
SqlQuery = sqlQuery,
|
||||
Title = "Title",
|
||||
Value = "Value",
|
||||
ValueClassName = "text-3xl",
|
||||
Color = "Color",
|
||||
Icon = "Icon",
|
||||
SubTitle = "SubTitle",
|
||||
ClassName = "mb-3",
|
||||
IsActive = true,
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Bir yolun arama motoru sozlesmesi. Ayri bir SEO tablosu yoktur: alanlar yolu tanimlayan
|
||||
/// kaydin (Route, Custom Component) ekranina eklenir, bu yuzden kume tek yerde durur.
|
||||
|
|
@ -337,3 +388,6 @@ public static class ListFormSeeder_DefaultJsons
|
|||
];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>KPI şeridindeki tek kart: başlık dil anahtarı, <c>Value</c> SQL ifadesi, <c>Where</c> ek koşul.</summary>
|
||||
public record WidgetCard(string Title, string Color, string Icon, string Where = null, string Value = "COUNT(*)");
|
||||
|
|
@ -79,6 +79,10 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = "AbpTenants",
|
||||
WidgetsJson = DefaultWidgetsJson("AbpTenants", listFormName, false,
|
||||
new("App.Widget.Total", "blue", "FaBuilding"),
|
||||
new("App.Widget.Active", "green", "FaCheckCircle", "\"IsActive\" = 'true'"),
|
||||
new("App.Widget.Passive", "gray", "FaTimesCircle", "\"IsActive\" = 'false'")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
SortMode = GridOptions.SortModeSingle,
|
||||
|
|
@ -625,6 +629,10 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Branch)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Branch)), listFormName, false,
|
||||
new("App.Widget.Total", "blue", "FaCodeBranch"),
|
||||
new("App.Widget.Active", "green", "FaCheckCircle", "\"IsActive\" = 'true'"),
|
||||
new("App.Widget.Passive", "gray", "FaTimesCircle", "(\"IsActive\" = 'false' OR \"IsActive\" IS NULL)")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
DefaultFilter = DefaultFilterJson,
|
||||
|
|
@ -1898,7 +1906,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName),
|
||||
SeedSyncInsert = false,
|
||||
SeedSyncUpdate = false,
|
||||
SeedSyncDelete = false,
|
||||
SeedSyncDelete = false,
|
||||
DefaultFilter = DefaultFilterJson,
|
||||
SortMode = GridOptions.SortModeSingle,
|
||||
FilterRowJson = DefaultFilterRowJson(),
|
||||
|
|
@ -2185,7 +2193,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName),
|
||||
SeedSyncInsert = false,
|
||||
SeedSyncUpdate = false,
|
||||
SeedSyncDelete = false,
|
||||
SeedSyncDelete = false,
|
||||
DefaultFilter = DefaultFilterJson,
|
||||
SortMode = GridOptions.SortModeSingle,
|
||||
FilterRowJson = DefaultFilterRowJson(),
|
||||
|
|
@ -2332,7 +2340,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName),
|
||||
SeedSyncInsert = false,
|
||||
SeedSyncUpdate = false,
|
||||
SeedSyncDelete = false,
|
||||
SeedSyncDelete = false,
|
||||
DefaultFilter = DefaultFilterJson,
|
||||
SortMode = GridOptions.SortModeSingle,
|
||||
FilterRowJson = DefaultFilterRowJson(),
|
||||
|
|
@ -4016,6 +4024,10 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Language)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Language)), listFormName, false,
|
||||
new("App.Widget.Total", "blue", "FaGlobe"),
|
||||
new("App.Widget.Active", "green", "FaCheckCircle", "\"IsEnabled\" = 'true'"),
|
||||
new("App.Widget.Passive", "gray", "FaTimesCircle", "\"IsEnabled\" = 'false'")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName),
|
||||
|
|
@ -4213,6 +4225,17 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.LanguageText)),
|
||||
WidgetsJson = DefaultWidgetsJson($@"
|
||||
SELECT 'App.Widget.Total' AS ""Title"", CAST(COUNT(DISTINCT ""Key"") AS VARCHAR(32)) AS ""Value"", 'purple' AS ""Color"", '{listFormName}' AS ""SubTitle"", 'FaKey' AS ""Icon""
|
||||
FROM ""{TableNameResolver.GetFullTableName(nameof(TableNameEnum.LanguageText))}""
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT l.""DisplayName"", CAST(COUNT(t.""Id"") AS VARCHAR(32)), 'blue', '{listFormName}', 'FaLanguage'
|
||||
FROM ""{TableNameResolver.GetFullTableName(nameof(TableNameEnum.LanguageText))}"" t
|
||||
JOIN ""{TableNameResolver.GetFullTableName(nameof(TableNameEnum.Language))}"" l ON l.""CultureName"" = t.""CultureName""
|
||||
GROUP BY l.""DisplayName""
|
||||
", 3),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName),
|
||||
|
|
@ -4553,6 +4576,11 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.ListForm)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.ListForm)), listFormName, false,
|
||||
new("App.Widget.Total", "blue", "FaThList"),
|
||||
new("App.Widget.List", "green", "FaTable", $"\"ListFormType\" = '{ListFormTypeEnum.List}'"),
|
||||
new("App.Widget.Form", "purple", "FaWpforms", $"\"ListFormType\" = '{ListFormTypeEnum.Form}'"),
|
||||
new("App.Widget.Chart", "orange", "FaChartBar", $"\"ListFormType\" = '{ListFormTypeEnum.Chart}'")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
SortMode = GridOptions.SortModeMultiple,
|
||||
|
|
@ -5235,6 +5263,10 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.BackgroundWorker)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.BackgroundWorker)), listFormName, false,
|
||||
new("App.Widget.Total", "blue", "FaClock"),
|
||||
new("App.Widget.Active", "green", "FaCheckCircle", "\"IsActive\" = 'true'"),
|
||||
new("App.Widget.Passive", "gray", "FaTimesCircle", "\"IsActive\" = 'false'")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName),
|
||||
|
|
@ -5531,6 +5563,11 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.View,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.BackgroundWorker_JobFlow)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.BackgroundWorker_JobFlow)), listFormName, false,
|
||||
new("App.Platform.Success", "green", "FaCheckCircle", "\"Status\" = 'Succeeded'"),
|
||||
new("App.Platform.Failed", "red", "FaTimesCircle", "\"Status\" = 'Failed'"),
|
||||
new("App.Widget.Processing", "blue", "FaSpinner", "\"Status\" = 'Processing'"),
|
||||
new("App.Widget.Enqueued", "yellow", "FaHourglassHalf", "\"Status\" IN ('Enqueued', 'Scheduled')")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
SortMode = GridOptions.SortModeSingle,
|
||||
|
|
@ -6055,6 +6092,10 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Menu)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Menu)), listFormName, false,
|
||||
new("App.Widget.Total", "blue", "FaBars"),
|
||||
new("App.Widget.Active", "green", "FaCheckCircle", "\"IsDisabled\" = 'false'"),
|
||||
new("App.Widget.Passive", "gray", "FaTimesCircle", "\"IsDisabled\" = 'true'")),
|
||||
KeyFieldName = "Code",
|
||||
KeyFieldDbSourceType = DbType.String,
|
||||
SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName),
|
||||
|
|
@ -6425,6 +6466,11 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.CustomEndpoint)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.CustomEndpoint)), listFormName, false,
|
||||
new("GET", "blue", "FaDownload", "\"Method\" = 'GET'"),
|
||||
new("POST", "green", "FaPlusCircle", "\"Method\" = 'POST'"),
|
||||
new("PUT", "orange", "FaEdit", "\"Method\" = 'PUT'"),
|
||||
new("DELETE", "red", "FaTrash", "\"Method\" = 'DELETE'")),
|
||||
SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName),
|
||||
SeedSyncInsert = true,
|
||||
SeedSyncUpdate = true,
|
||||
|
|
@ -7326,7 +7372,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
ListOrderNo = 6,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
||||
AllowSearch = true,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
|
|
@ -7411,6 +7457,10 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = mcpServerTable,
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.McpServer)), listFormName, false,
|
||||
new("App.Widget.Total", "blue", "FaServer"),
|
||||
new("App.Widget.Active", "green", "FaCheckCircle", "\"IsActive\" = 'true'"),
|
||||
new("App.Widget.Passive", "gray", "FaTimesCircle", "\"IsActive\" = 'false'")),
|
||||
SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName),
|
||||
SeedSyncInsert = true,
|
||||
SeedSyncUpdate = true,
|
||||
|
|
@ -8249,6 +8299,10 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Order)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Order)), listFormName, false,
|
||||
new("App.Widget.Total", "blue", "FaShoppingCart"),
|
||||
new("App.Widget.Active", "green", "FaCheckCircle", "\"IsActive\" = 'true'"),
|
||||
new("App.Widget.TotalRevenue", "purple", "FaMoneyBillWave", Value: "CAST(COALESCE(SUM(\"Total\"), 0) AS DECIMAL(18, 2))")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
DefaultFilter = DefaultFilterJson,
|
||||
|
|
@ -9436,6 +9490,10 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.ForumCategory)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.ForumCategory)), listFormName, true,
|
||||
new("App.Widget.Total", "blue", "FaFolder"),
|
||||
new("App.Widget.Active", "green", "FaCheckCircle", "\"IsActive\" = 'true'"),
|
||||
new("App.Listform.ListformField.IsLocked", "gray", "FaLock", "\"IsLocked\" = 'true'")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
DefaultFilter = DefaultFilterJson,
|
||||
|
|
@ -9657,6 +9715,11 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.ForumTopic)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.ForumTopic)), listFormName, true,
|
||||
new("App.Widget.Pinned", "purple", "FaThumbtack", "\"IsPinned\" = 'true'"),
|
||||
new("App.Listform.ListformField.IsSolved", "green", "FaCheckCircle", "\"IsSolved\" = 'true'"),
|
||||
new("App.Listform.ListformField.IsLocked", "gray", "FaLock", "\"IsLocked\" = 'true'"),
|
||||
new("App.Widget.TotalViews", "blue", "FaEye", Value: "COALESCE(SUM(\"ViewCount\"), 0)")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
DefaultFilter = DefaultFilterJson,
|
||||
|
|
@ -10327,6 +10390,10 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.ForumPost)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.ForumPost)), listFormName, true,
|
||||
new("App.Widget.Total", "blue", "FaComment"),
|
||||
new("App.Widget.AcceptedAnswers", "green", "FaCheckCircle", "\"IsAcceptedAnswer\" = 'true'"),
|
||||
new("App.Widget.Today", "purple", "FaCalendarDay", "CAST(\"CreationTime\" AS DATE) = CAST(CURRENT_TIMESTAMP AS DATE)")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
DefaultFilter = DefaultFilterJson,
|
||||
|
|
@ -11057,6 +11124,10 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.BlogPost)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.BlogPost)), listFormName, false,
|
||||
new("App.Widget.Published", "green", "FaCheckCircle", "\"IsPublished\" = 'true'"),
|
||||
new("App.Widget.Draft", "gray", "FaPencilAlt", "\"IsPublished\" = 'false'"),
|
||||
new("App.Widget.TotalViews", "purple", "FaEye", Value: "COALESCE(SUM(\"ViewCount\"), 0)")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
DefaultFilter = DefaultFilterJson,
|
||||
|
|
@ -11716,6 +11787,13 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
Description = $"{listFormName}.Description",
|
||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.DevRequest)),
|
||||
WidgetsJson = DefaultWidgetsJson(TableNameResolver.GetFullTableName(nameof(TableNameEnum.DevRequest)), listFormName, false,
|
||||
new("App.StaticLookup.Backlog", "gray", "FaInbox", "\"Status\" = 'Backlog'"),
|
||||
new("App.StaticLookup.Analysis", "pink", "FaSearch", "\"Status\" = 'Analysis'"),
|
||||
new("App.StaticLookup.ReadyToStart", "blue", "FaFlag", "\"Status\" = 'Ready to Start'"),
|
||||
new("App.StaticLookup.InProgress", "orange", "FaSpinner", "\"Status\" = 'In Progress'"),
|
||||
new("App.StaticLookup.Testing", "yellow", "FaVial", "\"Status\" = 'Testing'"),
|
||||
new("App.StaticLookup.Staging", "green", "FaRocket", "\"Status\" = 'Staging'")),
|
||||
KeyFieldName = "Id",
|
||||
KeyFieldDbSourceType = DbType.Guid,
|
||||
DefaultFilter = DefaultFilterJson,
|
||||
|
|
@ -11857,9 +11935,9 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
ValueExpr = "key",
|
||||
LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] {
|
||||
new () { Key="Backlog", Name="App.StaticLookup.Backlog" },
|
||||
new () { Key="Analysis", Name="App.StaticLookup.Analysis" },
|
||||
new () { Key="Ready to Start", Name="App.StaticLookup.ReadyToStart" },
|
||||
new () { Key="In Progress", Name="App.StaticLookup.InProgress" },
|
||||
new () { Key="Code Review", Name="App.StaticLookup.CodeReview" },
|
||||
new () { Key="Testing", Name="App.StaticLookup.Testing" },
|
||||
new () { Key="Staging", Name="App.StaticLookup.Staging" }
|
||||
}),
|
||||
|
|
@ -11909,7 +11987,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
CaptionName = "App.Listform.ListformField.Description",
|
||||
Width = 0,
|
||||
ListOrderNo = 6,
|
||||
Visible = true,
|
||||
Visible = false,
|
||||
IsActive = true,
|
||||
AllowSearch = true,
|
||||
ValidationRuleJson = DefaultValidationRuleRequiredJson,
|
||||
|
|
@ -11918,6 +11996,26 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
EditorOptions = EditorOptions.Html()
|
||||
},
|
||||
new() {
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.String,
|
||||
FieldName = "Analysis",
|
||||
EditGroupOrderNo = 1,
|
||||
EditOrderNo = 7,
|
||||
EditorType2 = EditorTypes.dxHtmlEditor,
|
||||
ColSpan = 2,
|
||||
CaptionName = "App.Listform.ListformField.Analysis",
|
||||
Width = 0,
|
||||
ListOrderNo = 15,
|
||||
Visible = false,
|
||||
IsActive = true,
|
||||
AllowSearch = true,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
EditorOptions = EditorOptions.Html()
|
||||
},
|
||||
|
||||
new() {
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
|
|
@ -12046,7 +12144,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region IntranetWidget
|
||||
listFormName = AppCodes.DeveloperKits.IntranetWidgets;
|
||||
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
||||
|
|
|
|||
|
|
@ -77,13 +77,17 @@ public static class EditorOptions
|
|||
=> New().Width(width).Height(height).Flag("multiple", multiple);
|
||||
|
||||
/// <summary>Zengin metin (HTML) editoru; tam arac cubugu ile.</summary>
|
||||
public static EditorOptionsBuilder Html(int height = 200)
|
||||
/// <remarks>
|
||||
/// <c>height</c> verilirse editor sabitlenir ve icerik kaydirilir; <c>min-height</c> ise
|
||||
/// en az yuksekligi korurken yeni satirlarla buyumesine izin verir.
|
||||
/// </remarks>
|
||||
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()) + "]}")
|
||||
.Flag("encodeHtml", true)
|
||||
.Height(height);
|
||||
.Raw("elementAttr", "{\"style\":\"min-height:" + EditorJson.Number(minHeight) + "px\"}");
|
||||
|
||||
private static IEnumerable<string> HtmlToolbarItems()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ public class DevRequest : FullAuditedEntity<Guid>
|
|||
public string? Title { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Analysis { get; set; }
|
||||
public DateTime? DueDate { get; set; }
|
||||
public string? Tags { get; set; }
|
||||
public string? Assignees { get; set; }
|
||||
|
|
|
|||
|
|
@ -1599,6 +1599,7 @@ public class PlatformDbContext :
|
|||
b.Property(e => e.Title).HasMaxLength(512).IsRequired();
|
||||
b.Property(e => e.Status).HasMaxLength(64).IsRequired();
|
||||
b.Property(e => e.Description).HasColumnType("nvarchar(max)");
|
||||
b.Property(e => e.Analysis).HasColumnType("nvarchar(max)");
|
||||
b.Property(e => e.Tags).HasMaxLength(256);
|
||||
b.Property(e => e.Assignees).HasMaxLength(512);
|
||||
b.Property(e => e.Priority).HasMaxLength(64);
|
||||
|
|
|
|||
|
|
@ -2375,6 +2375,9 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Analysis")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Assignees")
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
|
|
|||
|
|
@ -1322,6 +1322,7 @@ namespace Sozsoft.Platform.Migrations
|
|||
Title = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
||||
Status = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
Description = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Analysis = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DueDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
Tags = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||
Assignees = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
||||
|
|
|
|||
|
|
@ -2372,6 +2372,9 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Analysis")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Assignees")
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
|
|
|||
20
claude.md
20
claude.md
|
|
@ -121,6 +121,9 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
|||
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.
|
||||
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'ı. Ayrıntı: `lowcode.instructions.md` §8.1.
|
||||
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`.
|
||||
3. **Yeni ekran/menü** → yetki sözleşmesi olmadan önerilmez. Menü ve route aynı ekran
|
||||
|
|
@ -160,6 +163,11 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
|||
`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 (`App.Widget.*` önce). Kiracı
|
||||
tablosunda her kart `"TenantId" = '@TENANTID'` taşır; `IsDeleted` koşulu yazılmaz. Form/Chart,
|
||||
menüsüz alt form ve basit tanım listeleri istisnadır. Ayrıntı: `lowcode.instructions.md` §4.6.1.
|
||||
|
||||
### Cross-cutting: touching one thing means touching these too
|
||||
|
||||
|
|
@ -250,9 +258,15 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
|||
- **Talepteki görsel** → `App.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. Açıklama bu yüzden `update_row` ile yeniden yazılmaz, not
|
||||
`append_dev_request_note` ile eklenir. `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.
|
||||
`[image-N]` kalır. 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.
|
||||
- **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 `Backlog`/`Analysis` talebini analiz edip `Analysis`'e alır (hiçbir şey üretmez),
|
||||
`Ready to Start`'a kullanıcı taşır, ü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;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"ListFormCode": "App.Languages.LanguageText",
|
||||
"KeyFieldName": "Id",
|
||||
"GeneratedAt": "2026-09-24T19:17:56.868760Z",
|
||||
"GeneratedAt": "2026-09-24T19:17:56.86876Z",
|
||||
"Order": 2,
|
||||
"Rows": [
|
||||
{
|
||||
|
|
@ -30986,6 +30986,20 @@
|
|||
"Key": "App.Listform.ListformField.Description",
|
||||
"Value": "Açıklama"
|
||||
},
|
||||
{
|
||||
"Id": "76be8cec-c18d-5718-8063-e0c7f15615bc",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Listform.ListformField.Analysis",
|
||||
"Value": "Analysis"
|
||||
},
|
||||
{
|
||||
"Id": "095fb0a4-f3b8-5c6e-9bdb-d94ec1f6f496",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Listform.ListformField.Analysis",
|
||||
"Value": "Analiz"
|
||||
},
|
||||
{
|
||||
"Id": "9e6aa3ef-bf85-5719-a8cc-f3045f52a76d",
|
||||
"CultureName": "en",
|
||||
|
|
@ -42802,6 +42816,20 @@
|
|||
"Key": "App.ListFormFieldEdit.TodoDescriptionExpr",
|
||||
"Value": "Açıklama alanı"
|
||||
},
|
||||
{
|
||||
"Id": "7227ccdb-ab79-5d0c-a68f-3819603da4f1",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.ListFormFieldEdit.TodoAnalysisExpr",
|
||||
"Value": "Analysis Field"
|
||||
},
|
||||
{
|
||||
"Id": "3bad05d0-dab7-580d-9867-a3d580e73b91",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.ListFormFieldEdit.TodoAnalysisExpr",
|
||||
"Value": "Analiz alanı"
|
||||
},
|
||||
{
|
||||
"Id": "bb493ec4-ef38-5298-8910-e855501c9d65",
|
||||
"CultureName": "en",
|
||||
|
|
@ -42919,7 +42947,7 @@
|
|||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.ListFormFieldEdit.TodoStatusOrderPlaceholder",
|
||||
"Value": "Backlog, Ready to Start, In Progress, Code Review, Testing, Staging"
|
||||
"Value": "Backlog, Analysis, Ready to Start, In Progress, Testing, Staging"
|
||||
},
|
||||
{
|
||||
"Id": "d17288e1-c462-5856-8d13-0da438583e5e",
|
||||
|
|
@ -43166,6 +43194,20 @@
|
|||
"Key": "App.ListFormTodoBoard.DescriptionPlaceholder",
|
||||
"Value": "Todo açıklamasını yazın…"
|
||||
},
|
||||
{
|
||||
"Id": "3a16fde9-ed85-5c4e-bcfb-ea9b7a8009a8",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.ListFormTodoBoard.AnalysisPlaceholder",
|
||||
"Value": "Write the analysis of the request..."
|
||||
},
|
||||
{
|
||||
"Id": "f91ecec5-6334-5237-8f7b-89ce9683cca8",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.ListFormTodoBoard.AnalysisPlaceholder",
|
||||
"Value": "Talebin analizini yazın..."
|
||||
},
|
||||
{
|
||||
"Id": "c5b1a520-3a12-55f7-828c-9a5ef4d33051",
|
||||
"CultureName": "en",
|
||||
|
|
@ -45420,6 +45462,20 @@
|
|||
"Key": "App.StaticLookup.Backlog",
|
||||
"Value": "Bekleyen İşler"
|
||||
},
|
||||
{
|
||||
"Id": "c0cc1060-fd5d-5e4e-a052-8985b125732e",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.StaticLookup.Analysis",
|
||||
"Value": "Analysis"
|
||||
},
|
||||
{
|
||||
"Id": "16b66d78-6d17-5650-9831-fafbb76b408e",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.StaticLookup.Analysis",
|
||||
"Value": "Analiz"
|
||||
},
|
||||
{
|
||||
"Id": "2a645686-5b78-5a50-86fc-e6e4a44f72b1",
|
||||
"CultureName": "en",
|
||||
|
|
@ -45532,20 +45588,6 @@
|
|||
"Key": "App.Platform.Chart",
|
||||
"Value": "Grafik"
|
||||
},
|
||||
{
|
||||
"Id": "043893de-610e-55b5-a2a7-e4a5146672b9",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.StaticLookup.CodeReview",
|
||||
"Value": "Code Review"
|
||||
},
|
||||
{
|
||||
"Id": "3e6e364b-5b92-5489-8fee-83442eed3867",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.StaticLookup.CodeReview",
|
||||
"Value": "Kod İncelemesi"
|
||||
},
|
||||
{
|
||||
"Id": "dfa59c96-96b5-50f7-b462-229600230832",
|
||||
"CultureName": "en",
|
||||
|
|
@ -67190,6 +67232,20 @@
|
|||
"Key": "App.Mcp.Error.DevRequestNotReady",
|
||||
"Value": "\"{0}\" geliştirme talebi \"{1}\" durumunda; yalnızca \"Ready to Start\" durumundaki talepler başlatılabilir."
|
||||
},
|
||||
{
|
||||
"Id": "2b75ab25-37b8-56d3-b38e-ca5ba7296596",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Mcp.Error.DevRequestNotAnalyzable",
|
||||
"Value": "Development request \"{0}\" is in \"{1}\"; only requests in \"Backlog\" or \"Analysis\" can be analyzed."
|
||||
},
|
||||
{
|
||||
"Id": "4f36d621-7053-5d99-b952-37a64b4f63dc",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Mcp.Error.DevRequestNotAnalyzable",
|
||||
"Value": "\"{0}\" geliştirme talebi \"{1}\" durumunda; yalnızca \"Backlog\" ya da \"Analysis\" durumundaki talepler analiz edilebilir."
|
||||
},
|
||||
{
|
||||
"Id": "6cac6a6b-9329-5552-86d9-08e602afcb0f",
|
||||
"CultureName": "en",
|
||||
|
|
@ -67204,6 +67260,20 @@
|
|||
"Key": "App.Mcp.Error.DevRequestNoteRequired",
|
||||
"Value": "Eklenecek not boş olamaz."
|
||||
},
|
||||
{
|
||||
"Id": "d3c1c3fe-5dda-5113-8f47-515d5c91caab",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Mcp.Error.DevRequestAnalysisRequired",
|
||||
"Value": "The analysis cannot be empty."
|
||||
},
|
||||
{
|
||||
"Id": "ada842cf-97ef-544f-8993-504e450895ac",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Mcp.Error.DevRequestAnalysisRequired",
|
||||
"Value": "Analiz boş olamaz."
|
||||
},
|
||||
{
|
||||
"Id": "aa72e998-f4a1-5873-95ef-1fcfe0b601f9",
|
||||
"CultureName": "en",
|
||||
|
|
@ -69261,6 +69331,370 @@
|
|||
"ResourceName": "Platform",
|
||||
"Key": "App.Mcp.Error.VerifyWizardPending",
|
||||
"Value": "\"{0}\" wizard'ı henüz kurulmadı: seed dosyaları yazıldı ama DB Migrate çalışmadı. Kullanıcıdan Wizard Manager'da DB Migrate çalıştırmasını isteyin, sonra tekrar doğrulayın."
|
||||
},
|
||||
{
|
||||
"Id": "cfb9483e-f6b1-5041-af8f-a7b36b1c3d1b",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Total",
|
||||
"Value": "Total"
|
||||
},
|
||||
{
|
||||
"Id": "dcee1716-6f7b-5480-80f2-20c63f18cee3",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Total",
|
||||
"Value": "Toplam"
|
||||
},
|
||||
{
|
||||
"Id": "5b7ac5a9-0d19-56c8-b510-84dff08812b2",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Active",
|
||||
"Value": "Active"
|
||||
},
|
||||
{
|
||||
"Id": "5828ae1a-9592-56c3-aa72-192f873bcdde",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Active",
|
||||
"Value": "Aktif"
|
||||
},
|
||||
{
|
||||
"Id": "0d934934-b906-5656-91d4-845ed25a226f",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Passive",
|
||||
"Value": "Passive"
|
||||
},
|
||||
{
|
||||
"Id": "4794dbc8-dff2-52ea-8f0a-c7e45492454d",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Passive",
|
||||
"Value": "Pasif"
|
||||
},
|
||||
{
|
||||
"Id": "1c187a6c-50b1-5817-bffe-d43504316d09",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Published",
|
||||
"Value": "Published"
|
||||
},
|
||||
{
|
||||
"Id": "249cd74e-5d8d-59b4-ab1b-0e1e6027d059",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Published",
|
||||
"Value": "Yayında"
|
||||
},
|
||||
{
|
||||
"Id": "2e85ac8f-0013-5615-9303-1356666bf5b4",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Draft",
|
||||
"Value": "Draft"
|
||||
},
|
||||
{
|
||||
"Id": "cac1c52e-3a9a-5619-b630-9efc36bf570b",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Draft",
|
||||
"Value": "Taslak"
|
||||
},
|
||||
{
|
||||
"Id": "5a95141c-7795-59db-93c0-79de85bf2ec4",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.TotalViews",
|
||||
"Value": "Total Views"
|
||||
},
|
||||
{
|
||||
"Id": "06d4dd8f-b635-51a5-952c-634f07f63872",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.TotalViews",
|
||||
"Value": "Toplam Görüntülenme"
|
||||
},
|
||||
{
|
||||
"Id": "3733d251-e6ec-5185-a81d-9d5f243c7481",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.TotalRevenue",
|
||||
"Value": "Total Revenue"
|
||||
},
|
||||
{
|
||||
"Id": "4e9523f9-e134-51e5-b9ab-9c1d942de904",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.TotalRevenue",
|
||||
"Value": "Toplam Tutar"
|
||||
},
|
||||
{
|
||||
"Id": "dc0fc005-26b7-5ab3-a9e0-025b0c244f5e",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Processing",
|
||||
"Value": "Processing"
|
||||
},
|
||||
{
|
||||
"Id": "b6a42a49-550b-5507-924c-be549aafa17c",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Processing",
|
||||
"Value": "İşleniyor"
|
||||
},
|
||||
{
|
||||
"Id": "f59e997a-22a9-5272-91ef-74f169460b1f",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Enqueued",
|
||||
"Value": "Queued"
|
||||
},
|
||||
{
|
||||
"Id": "c1cab658-b676-5960-83ad-d80faead0ef2",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Enqueued",
|
||||
"Value": "Kuyrukta"
|
||||
},
|
||||
{
|
||||
"Id": "cb70d1eb-f81c-5ecc-960c-07259fa162b5",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.List",
|
||||
"Value": "List"
|
||||
},
|
||||
{
|
||||
"Id": "7c7cc937-d0b4-5a22-aa93-efba03a22f2c",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.List",
|
||||
"Value": "Liste"
|
||||
},
|
||||
{
|
||||
"Id": "0bd15ad4-59df-5eab-b18b-7108d5fbf31c",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Form",
|
||||
"Value": "Form"
|
||||
},
|
||||
{
|
||||
"Id": "90a05ae9-611a-50e1-913f-8d5c2d011017",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Form",
|
||||
"Value": "Form"
|
||||
},
|
||||
{
|
||||
"Id": "2a36f006-fb79-5371-bcc9-85f9665ffe6d",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Chart",
|
||||
"Value": "Chart"
|
||||
},
|
||||
{
|
||||
"Id": "9e91c54f-5e6f-53c0-838c-08023c5d2999",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Chart",
|
||||
"Value": "Grafik"
|
||||
},
|
||||
{
|
||||
"Id": "f14c54a8-6204-58d3-8e34-99dd6772ebf7",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Today",
|
||||
"Value": "Today"
|
||||
},
|
||||
{
|
||||
"Id": "4f4dc6ac-76da-533a-a9d0-0bff9a599429",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Today",
|
||||
"Value": "Bugün"
|
||||
},
|
||||
{
|
||||
"Id": "a5f51fca-5708-55fd-b4c1-86a4a09f6f49",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Errors",
|
||||
"Value": "Errors"
|
||||
},
|
||||
{
|
||||
"Id": "097213dd-41a9-59a0-8580-455344be487a",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Errors",
|
||||
"Value": "Hatalar"
|
||||
},
|
||||
{
|
||||
"Id": "32d44619-87d5-52a1-ad52-18ca0ae05f42",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.SignedInToday",
|
||||
"Value": "Signed In Today"
|
||||
},
|
||||
{
|
||||
"Id": "9ce0f85e-40e6-5537-a21c-52e519a59b18",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.SignedInToday",
|
||||
"Value": "Bugün Giriş Yapan"
|
||||
},
|
||||
{
|
||||
"Id": "392cffdf-de41-51ed-b54a-25b3740ece59",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.ActiveToday",
|
||||
"Value": "Active Today"
|
||||
},
|
||||
{
|
||||
"Id": "a64fc55c-a100-5b82-9ea0-b9f4821b2e93",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.ActiveToday",
|
||||
"Value": "Bugün Aktif"
|
||||
},
|
||||
{
|
||||
"Id": "db419b9f-31ef-5ba0-8a92-4b4e82d126fc",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Pinned",
|
||||
"Value": "Pinned"
|
||||
},
|
||||
{
|
||||
"Id": "40a10314-e283-5357-a1e5-e26b3d06faf3",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Pinned",
|
||||
"Value": "Sabitlenmiş"
|
||||
},
|
||||
{
|
||||
"Id": "efe1415e-db82-5812-8705-4672799a58ab",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Expired",
|
||||
"Value": "Expired"
|
||||
},
|
||||
{
|
||||
"Id": "75ec191d-a7cf-571d-b2c5-cb2f8c046c21",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Expired",
|
||||
"Value": "Süresi Dolmuş"
|
||||
},
|
||||
{
|
||||
"Id": "b43721ea-2ab7-5522-a7c4-96fa1b406e05",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.TotalResponses",
|
||||
"Value": "Total Responses"
|
||||
},
|
||||
{
|
||||
"Id": "19389bec-2911-5e15-934f-504f1542955d",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.TotalResponses",
|
||||
"Value": "Toplam Yanıt"
|
||||
},
|
||||
{
|
||||
"Id": "4328b107-b212-51d1-9d4a-a4803fa1cc0d",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.TotalLikes",
|
||||
"Value": "Total Likes"
|
||||
},
|
||||
{
|
||||
"Id": "da98e932-8f63-5b08-85cd-3814963833f0",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.TotalLikes",
|
||||
"Value": "Toplam Beğeni"
|
||||
},
|
||||
{
|
||||
"Id": "98fbfb84-ac4d-5dc1-8acd-3f3d2b4c3b01",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Upcoming",
|
||||
"Value": "Upcoming"
|
||||
},
|
||||
{
|
||||
"Id": "96ee24d2-a8cf-53c7-aa68-bc208a1b7a6c",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Upcoming",
|
||||
"Value": "Yaklaşan"
|
||||
},
|
||||
{
|
||||
"Id": "ca076402-bf1c-5fee-a38e-44e0c8904dab",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Sent",
|
||||
"Value": "Sent"
|
||||
},
|
||||
{
|
||||
"Id": "37d86092-7c5b-5bfd-ae10-7470c63f5347",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Sent",
|
||||
"Value": "Gönderildi"
|
||||
},
|
||||
{
|
||||
"Id": "a50e4ec1-03ce-5c61-88c6-2854ba91d762",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Pending",
|
||||
"Value": "Pending"
|
||||
},
|
||||
{
|
||||
"Id": "e7ddfd18-5a70-5ba6-b72a-228330f0372a",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Pending",
|
||||
"Value": "Bekliyor"
|
||||
},
|
||||
{
|
||||
"Id": "92f99a0d-c8aa-5a0f-b006-180b1072640e",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Unread",
|
||||
"Value": "Unread"
|
||||
},
|
||||
{
|
||||
"Id": "64d6688e-9d99-5794-8ade-643ddd8a38b9",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Unread",
|
||||
"Value": "Okunmadı"
|
||||
},
|
||||
{
|
||||
"Id": "ca8789e7-ffdc-502e-80db-62d72e745be0",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.AcceptedAnswers",
|
||||
"Value": "Accepted Answers"
|
||||
},
|
||||
{
|
||||
"Id": "92880cf9-e12e-5f94-a905-74e45e9af69e",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.AcceptedAnswers",
|
||||
"Value": "Kabul Edilen Cevaplar"
|
||||
},
|
||||
{
|
||||
"Id": "55d48500-09ce-5b61-bcdf-448f27cbdd44",
|
||||
"CultureName": "en",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Unverified",
|
||||
"Value": "Unverified"
|
||||
},
|
||||
{
|
||||
"Id": "3b9d9693-bcb7-5c95-8bd5-c57bf6b4b5d4",
|
||||
"CultureName": "tr",
|
||||
"ResourceName": "Platform",
|
||||
"Key": "App.Widget.Unverified",
|
||||
"Value": "Doğrulanmamış"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -459,6 +459,7 @@ export interface TodoOptionDto {
|
|||
titleExpr?: string
|
||||
statusExpr?: string
|
||||
descriptionExpr?: string
|
||||
analysisExpr?: string
|
||||
dueDateExpr?: string
|
||||
tagExpr?: string
|
||||
assigneeExpr?: string
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ const mappings: Array<{
|
|||
{ name: 'titleExpr', labelKey: 'TodoTitleExpr', required: true },
|
||||
{ name: 'statusExpr', labelKey: 'TodoStatusExpr', required: true },
|
||||
{ name: 'descriptionExpr', labelKey: 'TodoDescriptionExpr' },
|
||||
{ name: 'analysisExpr', labelKey: 'TodoAnalysisExpr' },
|
||||
{ name: 'dueDateExpr', labelKey: 'TodoDueDateExpr' },
|
||||
{ name: 'tagExpr', labelKey: 'TodoTagExpr' },
|
||||
{ name: 'assigneeExpr', labelKey: 'TodoAssigneeExpr' },
|
||||
|
|
|
|||
|
|
@ -137,13 +137,14 @@ const initialValues: ListFormWizardDto = {
|
|||
titleExpr: '',
|
||||
statusExpr: '',
|
||||
descriptionExpr: '',
|
||||
analysisExpr: '',
|
||||
dueDateExpr: '',
|
||||
tagExpr: '',
|
||||
assigneeExpr: '',
|
||||
priorityExpr: '',
|
||||
completedExpr: '',
|
||||
orderExpr: '',
|
||||
statusOrder: 'Backlog, Ready to Start, In Progress, Code Review, Testing, Staging',
|
||||
statusOrder: 'Backlog, Analysis, Ready to Start, In Progress, Testing, Staging',
|
||||
allowDragging: true,
|
||||
},
|
||||
workflowDto: {
|
||||
|
|
|
|||
|
|
@ -55,6 +55,10 @@ const mappings: Array<{
|
|||
name: 'descriptionExpr',
|
||||
translationKey: '::App.ListFormFieldEdit.TodoDescriptionExpr',
|
||||
},
|
||||
{
|
||||
name: 'analysisExpr',
|
||||
translationKey: '::App.ListFormFieldEdit.TodoAnalysisExpr',
|
||||
},
|
||||
{
|
||||
name: 'dueDateExpr',
|
||||
translationKey: '::App.ListFormFieldEdit.TodoDueDateExpr',
|
||||
|
|
|
|||
|
|
@ -78,15 +78,15 @@ interface UserOption {
|
|||
|
||||
type LookupDisplayValues = Record<string, Map<string, string>>
|
||||
|
||||
type DetailTab = 'description' | 'timesheet' | 'subtask'
|
||||
type DetailTab = 'description' | 'analysis' | 'timesheet' | 'subtask'
|
||||
|
||||
type JsonRow = Record<string, any>
|
||||
|
||||
const TODO_STATUS_LANGUAGE_KEYS: Record<string, string> = {
|
||||
backlog: 'App.StaticLookup.Backlog',
|
||||
analysis: 'App.StaticLookup.Analysis',
|
||||
'ready to start': 'App.StaticLookup.ReadyToStart',
|
||||
'in progress': 'App.StaticLookup.InProgress',
|
||||
'code review': 'App.StaticLookup.CodeReview',
|
||||
testing: 'App.StaticLookup.Testing',
|
||||
staging: 'App.StaticLookup.Staging',
|
||||
}
|
||||
|
|
@ -972,6 +972,7 @@ const TodoBoardContent = ({
|
|||
options.titleExpr,
|
||||
options.statusExpr,
|
||||
options.descriptionExpr,
|
||||
options.analysisExpr,
|
||||
options.dueDateExpr,
|
||||
options.tagExpr,
|
||||
options.assigneeExpr,
|
||||
|
|
@ -1038,6 +1039,12 @@ const TodoBoardContent = ({
|
|||
label: translate('::App.Listform.ListformField.Description'),
|
||||
}
|
||||
: undefined,
|
||||
options.analysisExpr
|
||||
? {
|
||||
key: 'analysis' as DetailTab,
|
||||
label: translate('::App.Listform.ListformField.Analysis'),
|
||||
}
|
||||
: undefined,
|
||||
options.timesheetExpr
|
||||
? {
|
||||
key: 'timesheet' as DetailTab,
|
||||
|
|
@ -1136,6 +1143,21 @@ const TodoBoardContent = ({
|
|||
}
|
||||
/>
|
||||
)}
|
||||
{activeTab === 'analysis' && options.analysisExpr && (
|
||||
<HtmlEditor
|
||||
{...htmlEditorDefaults}
|
||||
height="100%"
|
||||
placeholder={translate('::App.ListFormTodoBoard.AnalysisPlaceholder')}
|
||||
readOnly={!canUpdate}
|
||||
value={String(draft[options.analysisExpr] ?? '')}
|
||||
onValueChanged={(event) =>
|
||||
setDraft((current) => ({
|
||||
...current,
|
||||
[options.analysisExpr!]: event.value,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{activeTab === 'timesheet' && options.timesheetExpr && (
|
||||
<div className="flex min-h-0 flex-1 flex-col gap-2">
|
||||
{hasInvalidJson(fieldValue(draft, options.timesheetExpr)) && (
|
||||
|
|
|
|||
Loading…
Reference in a new issue