Wizard ile ListFormComponent arasında bağlantı kuruldu.
This commit is contained in:
parent
4d7aaef901
commit
3622764923
41 changed files with 14096 additions and 2416 deletions
11
.github/instructions/dotnet.instructions.md
vendored
11
.github/instructions/dotnet.instructions.md
vendored
|
|
@ -100,7 +100,16 @@ Ek kurallar:
|
||||||
|
|
||||||
- **Katman sınırı**: Domain.Shared → Domain → Application.Contracts → Application →
|
- **Katman sınırı**: Domain.Shared → Domain → Application.Contracts → Application →
|
||||||
HttpApi → HttpApi.Host. Ters yönde referans verilmez; `EntityFrameworkCore` yalnızca
|
HttpApi → HttpApi.Host. Ters yönde referans verilmez; `EntityFrameworkCore` yalnızca
|
||||||
Domain'e bakar.
|
Domain'e bakar. `DbMigrator` yalnızca `Application.Contracts` + `EntityFrameworkCore`
|
||||||
|
referansı taşır; **Application'a referans verilmez** — seeder'ın uygulama servislerine
|
||||||
|
ihtiyaç duyması, kodun yanlış katmanda olduğunun işaretidir.
|
||||||
|
- **Hem seeder'ın hem AppService'in ihtiyaç duyduğu dönüşüm**: ortak kod
|
||||||
|
`Application.Contracts`'a konur (DTO'nun kendi metodu olarak; yeni yardımcı sınıf
|
||||||
|
üretilmez). Dönüşümün entity'ye yazması gerekiyorsa entity'nin **yazılabilir kolon
|
||||||
|
sözleşmesi** Domain.Shared'de bir arayüz olarak tanımlanır (`IListFormArtifact`,
|
||||||
|
`IListFormFieldArtifact`), entity bu arayüzü uygular ve dönüşüm arayüz üzerinden yazar.
|
||||||
|
Böylece Contracts, Domain'i tanımadan entity doldurabilir ve aynı dönüşüm iki yerde
|
||||||
|
kopyalanmaz.
|
||||||
- **AppService**: `PlatformAppService`'ten türer, arayüzü Application.Contracts'ta
|
- **AppService**: `PlatformAppService`'ten türer, arayüzü Application.Contracts'ta
|
||||||
tanımlanır. Controller yazılmaz — ABP otomatik API üretir. Controller sadece ABP
|
tanımlanır. Controller yazılmaz — ABP otomatik API üretir. Controller sadece ABP
|
||||||
konvansiyonunun karşılamadığı durumda (dosya indirme, harici webhook) eklenir.
|
konvansiyonunun karşılamadığı durumda (dosya indirme, harici webhook) eklenir.
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,11 @@ sorusunda buraya bak.
|
||||||
|
|
||||||
**Ortak kurallar**
|
**Ortak kurallar**
|
||||||
|
|
||||||
- Bütün `*Json` kolonları veritabanında **tek satır JSON string** olarak durur; wizard seed
|
- Bütün `*Json` kolonları veritabanında **tek satır JSON string** olarak durur. Wizard seed
|
||||||
dosyasına yazarken kaçışlanır.
|
dosyasının `ListForm`/`Fields` bölümleri ekranın DTO sözleşmesini taşıdığı için orada bu
|
||||||
|
kolonlar kaçışlanmış metin değil, tipli karşılıkları (`SchedulerOptionJson` →
|
||||||
|
`SchedulerOptionDto`) olarak yer alır; kaçışlama yalnızca `EditorOptions`, `EditorScript`,
|
||||||
|
`LookupQuery` gibi gerçekten metin olan alanlar içindir.
|
||||||
- Varsayılan değeri olan bir alanı tekrar yazmak zorunda değilsin; yalnızca farklı olanı yaz.
|
- Varsayılan değeri olan bir alanı tekrar yazmak zorunda değilsin; yalnızca farklı olanı yaz.
|
||||||
- `Accepted Values` yazan yerlerde değer birebir eşleşmelidir (büyük/küçük harf dahil).
|
- `Accepted Values` yazan yerlerde değer birebir eşleşmelidir (büyük/küçük harf dahil).
|
||||||
- **Bilinmeyen anahtar sessizce düşer — tarayıcıya hiç ulaşmaz.** `GridOptionsDto` bu 47
|
- **Bilinmeyen anahtar sessizce düşer — tarayıcıya hiç ulaşmaz.** `GridOptionsDto` bu 47
|
||||||
|
|
|
||||||
139
.github/instructions/lowcode.instructions.md
vendored
139
.github/instructions/lowcode.instructions.md
vendored
|
|
@ -53,7 +53,7 @@ configs/seeds/host/
|
||||||
├── postgres/object|execute/… ← aynısının PostgreSQL diyalekti (hedef PG ise)
|
├── postgres/object|execute/… ← aynısının PostgreSQL diyalekti (hedef PG ise)
|
||||||
├── crud/{EntityName}.json ← tablonun REST uçları
|
├── crud/{EntityName}.json ← tablonun REST uçları
|
||||||
├── custom/{ComponentName}.json ← Custom Component (Visual Designer dokümanı ya da kod)
|
├── custom/{ComponentName}.json ← Custom Component (Visual Designer dokümanı ya da kod)
|
||||||
└── wizard/{yyyyMMddHHmmss}_{WizardName}.json ← ekran + menü + yetki + dil anahtarları
|
└── wizard/{WizardName}.json ← ekran + menü + yetki + dil anahtarları
|
||||||
```
|
```
|
||||||
|
|
||||||
Tenant kapsamı isteniyorsa `host/` yerine `tenants/{tenantId}/`; iç düzen aynıdır.
|
Tenant kapsamı isteniyorsa `host/` yerine `tenants/{tenantId}/`; iç düzen aynıdır.
|
||||||
|
|
@ -65,13 +65,14 @@ Tenant kapsamı isteniyorsa `host/` yerine `tenants/{tenantId}/`; iç düzen ayn
|
||||||
| 1 | Tablo / view | `sql/object/…` | Ekranın bağlanacağı nesne önce var olmalı |
|
| 1 | Tablo / view | `sql/object/…` | Ekranın bağlanacağı nesne önce var olmalı |
|
||||||
| 2 | CRUD uçları (gerekiyorsa) | `crud/{Entity}.json` | Custom Component'in data source'ları buna bakar |
|
| 2 | CRUD uçları (gerekiyorsa) | `crud/{Entity}.json` | Custom Component'in data source'ları buna bakar |
|
||||||
| 3 | Custom Component (gerekiyorsa) | `custom/{Name}.json` | Wizard `Custom` yolunda bileşen adını arar |
|
| 3 | Custom Component (gerekiyorsa) | `custom/{Name}.json` | Wizard `Custom` yolunda bileşen adını arar |
|
||||||
| 4 | Wizard | `wizard/{ts}_{Ad}.json` | Menü, yetki, dil anahtarı ve ListForm'u üretir |
|
| 4 | Wizard | `wizard/{Ad}.json` | Menü, yetki, dil anahtarı ve ListForm'u üretir |
|
||||||
| 5 | Alt ekranlar (varsa) | ek `wizard/…` dosyaları | Ana wizard'dan **önceki** zaman damgasını alır |
|
| 5 | Alt ekranlar (varsa) | ek `wizard/…` dosyaları | Ana wizard'dan **önceki** `SeededAt` damgasını alır |
|
||||||
|
|
||||||
**Zaman damgası bir bağımlılık aracıdır.** `WizardDataSeeder` `wizard/` klasörünü **dosya adına
|
**`Wizard.SeededAt` bir bağımlılık aracıdır.** Dosya adı tarih taşımaz (`{WizardName}.json`);
|
||||||
göre sıralı** işler. Bir wizard başka bir wizard'ın ürettiği menüye/ListForm'a dayanıyorsa,
|
`WizardDataSeeder` `wizard/` klasörünü **`Wizard.SeededAt` damgasına göre sıralı** işler (eşitlikte
|
||||||
bağımlı olanın zaman damgası **daha büyük** olmalıdır. Alt form olarak kullanılacak menüsüz
|
dosya adı). Bir wizard başka bir wizard'ın ürettiği menüye/ListForm'a dayanıyorsa, bağımlı olanın
|
||||||
ekranı her zaman önce numaralandır.
|
damgası **daha büyük** olmalıdır. Alt form olarak kullanılacak menüsüz ekranı her zaman önce
|
||||||
|
damgala.
|
||||||
|
|
||||||
### 0.3 Yazmadan önce zorunlu keşif
|
### 0.3 Yazmadan önce zorunlu keşif
|
||||||
|
|
||||||
|
|
@ -102,7 +103,7 @@ Uydurma. Şu üçünü mutlaka kontrol et:
|
||||||
| JSON alan adları | **PascalCase** (`Wizard`, `ListFormCode`, `Groups`) — seeder `PropertyNameCaseInsensitive` okur ama dosyalar PascalCase üretilir |
|
| JSON alan adları | **PascalCase** (`Wizard`, `ListFormCode`, `Groups`) — seeder `PropertyNameCaseInsensitive` okur ama dosyalar PascalCase üretilir |
|
||||||
| Custom component dosyası | Kök alanlar PascalCase; `Props` **içindeki** designer dokümanı camelCase (`visualDesigner`, `nodes`, `sourceMode`) |
|
| Custom component dosyası | Kök alanlar PascalCase; `Props` **içindeki** designer dokümanı camelCase (`visualDesigner`, `nodes`, `sourceMode`) |
|
||||||
| Enum'lar | Sayı olarak yazılır (§3) |
|
| Enum'lar | Sayı olarak yazılır (§3) |
|
||||||
| `GeneratedAt` | ISO-8601 UTC (`2026-08-26T10:15:00Z`) — yalnızca bilgi amaçlı. **Yalnızca `custom/` ve `crud/` dosyalarında vardır**; wizard seed dosyasında böyle bir alan yoktur (zaman damgası dosya adındadır) |
|
| `GeneratedAt` | ISO-8601 UTC (`2026-08-26T10:15:00Z`) — yalnızca bilgi amaçlı. **Yalnızca `custom/` ve `crud/` dosyalarında vardır**; wizard seed dosyasında karşılığı `Wizard.SeededAt` alanıdır |
|
||||||
| İç içe JSON | `EditorOptions`, `EditorScript`, `LookupQuery` → JSON **string** olarak kaçışlanır |
|
| İç içe JSON | `EditorOptions`, `EditorScript`, `LookupQuery` → JSON **string** olarak kaçışlanır |
|
||||||
| Satır sonu | LF |
|
| Satır sonu | LF |
|
||||||
|
|
||||||
|
|
@ -466,7 +467,7 @@ Tutarlılık zorunludur; menü, route, yetki ve dil anahtarı aynı kökten tür
|
||||||
| Dil anahtarı (alan) | `App.Listform.ListformField.{Alan}` | `App.Listform.ListformField.OrderNo` |
|
| Dil anahtarı (alan) | `App.Listform.ListformField.{Alan}` | `App.Listform.ListformField.OrderNo` |
|
||||||
| Route (List) | `/admin/list/{ListFormCode}` — statik `:listFormCode` route'u karşılar, DB'ye kayıt yazılmaz | `/admin/list/App.Mrp.Orders` |
|
| Route (List) | `/admin/list/{ListFormCode}` — statik `:listFormCode` route'u karşılar, DB'ye kayıt yazılmaz | `/admin/list/App.Mrp.Orders` |
|
||||||
| Route (Custom) | Bileşenin `RoutePath` değeri; route doğrudan bundan üretilir | `/admin/order-board` |
|
| Route (Custom) | Bileşenin `RoutePath` değeri; route doğrudan bundan üretilir | `/admin/order-board` |
|
||||||
| Wizard seed dosyası | `{yyyyMMddHHmmss}_{WizardName}.json` | `20260826101500_Orders.json` |
|
| Wizard seed dosyası | `{WizardName}.json` | `Orders.json` |
|
||||||
| Custom component seed | `{Name}.json` | `OrderBoard.json` |
|
| Custom component seed | `{Name}.json` | `OrderBoard.json` |
|
||||||
| CRUD endpoint seed | `{EntityName}.json` | `Mrp_T_Order.json` |
|
| CRUD endpoint seed | `{EntityName}.json` | `Mrp_T_Order.json` |
|
||||||
|
|
||||||
|
|
@ -561,14 +562,26 @@ Her editörün kendine özgü seçenekleri için §4.3.2.
|
||||||
|
|
||||||
## 4. Artefakt A — ListForm ekranı (Wizard seed dosyası)
|
## 4. Artefakt A — ListForm ekranı (Wizard seed dosyası)
|
||||||
|
|
||||||
Konum: `configs/seeds/{kapsam}/wizard/{yyyyMMddHHmmss}_{WizardName}.json`
|
Konum: `configs/seeds/{kapsam}/wizard/{WizardName}.json` (dosya adında tarih yoktur)
|
||||||
Okuyan: `WizardDataSeeder` · Yazan: `ListFormWizardAppService`
|
Okuyan: `WizardDataSeeder` · Yazan: `ListFormWizardAppService` ve `WizardSeedSynchronizer`
|
||||||
|
|
||||||
### 4.1 Dosya iskeleti
|
### 4.1 Dosya iskeleti
|
||||||
|
|
||||||
```jsonc
|
```jsonc
|
||||||
{
|
{
|
||||||
"Wizard": { /* ListFormWizardDto — 4.2 */ },
|
"Wizard": { // yalnızca ekrandan üretilemeyen bilgiler — 4.2
|
||||||
|
"ComponentKind": 0, "WizardName": "Tags", "SeededAt": "2026-09-01T09:03:00",
|
||||||
|
"WizardCode": "App.Prj.Tags", "CustomComponentName": "",
|
||||||
|
"PermissionGroupName": "App.Prj", "PermissionGroupDisplayNameEn": "…", "PermissionGroupDisplayNameTr": "…",
|
||||||
|
"DataSourceConnectionString": "", "ModuleId": "Prj",
|
||||||
|
"Menu": { "MenuCode": "…", "MenuOrder": 2, "MenuUrl": "…", "MenuIcon": "…",
|
||||||
|
"MenuParentCode": "…", "MenuParentIcon": "…" }, // "CreateMenu": false ise eklenir
|
||||||
|
"Language": { "LanguageTextMenuEn": "…", /* Menu/Title/Desc/MenuParent · EN + TR */ },
|
||||||
|
"Groups": [ { "Items": [ { "FieldName": "…", "CaptionName": "…",
|
||||||
|
"EnglishCaption": "…", "TurkishCaption": "…" } ] } ]
|
||||||
|
},
|
||||||
|
"ListForm": { /* GridOptionsEditDto — ekranın kendisi, ListForm detayıyla aynı sözleşme */ },
|
||||||
|
"Fields": [ /* ColumnFormatEditDto — alanlar, alan detayıyla aynı sözleşme */ ],
|
||||||
"IsDeletedField": true, // tabloda IsDeleted var mı (soft delete)
|
"IsDeletedField": true, // tabloda IsDeleted var mı (soft delete)
|
||||||
"IsCreatedField": true, // tabloda CreatorId var mı (audit)
|
"IsCreatedField": true, // tabloda CreatorId var mı (audit)
|
||||||
"InsertedRecords": { // bu çalıştırmanın GERÇEKTEN yarattığı kayıtlar
|
"InsertedRecords": { // bu çalıştırmanın GERÇEKTEN yarattığı kayıtlar
|
||||||
|
|
@ -578,19 +591,50 @@ Okuyan: `WizardDataSeeder` · Yazan: `ListFormWizardAppService`
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`Wizard` bölümü yalnızca ekrandan üretilemeyen bilgileri taşır (menü, yetki, dil metinleri,
|
||||||
|
bileşen yolu, alan başlıklarının dil karşılıkları); `ListForm`/`Fields` içinde karşılığı olan
|
||||||
|
hiçbir alan orada tekrar edilmez (§4.2).
|
||||||
|
|
||||||
|
**`ListForm` + `Fields` ekranın tek gerçeğidir.** Seeder ekranı bu iki bölümden kurar; `Wizard`
|
||||||
|
bölümünden yalnızca menü, yetki, dil anahtarları ve veri kaynağı üretilir. Ekran **ListForm
|
||||||
|
detayından** (liste ayarları, alan ayarları, JSON satır editörleri) düzenlendiğinde sunucu aynı
|
||||||
|
dosyanın `ListForm`/`Fields` bölümlerini ve `Wizard` bölümündeki karşılıklarını geri yazar
|
||||||
|
(`WizardSeedSynchronizer`); böylece wizard yeniden deploy edildiğinde tasarımcıdaki iş kaybolmaz.
|
||||||
|
Elle bu dosyayı yazarken `ListForm`/`Fields` bölümlerini uydurma — wizard'ı deploy et, dosyayı
|
||||||
|
sunucu üretsin.
|
||||||
|
|
||||||
`InsertedRecords` **silme sözleşmesidir**: wizard silindiğinde yalnızca burada listelenen
|
`InsertedRecords` **silme sözleşmesidir**: wizard silindiğinde yalnızca burada listelenen
|
||||||
kayıtlar silinir, paylaşılan kayıtlara (var olan izin grubu, var olan üst menü) dokunulmaz.
|
kayıtlar silinir, paylaşılan kayıtlara (var olan izin grubu, var olan üst menü) dokunulmaz.
|
||||||
Elle dosya yazarken buraya var olan bir kaydı koyma — silme onu da götürür.
|
Elle dosya yazarken buraya var olan bir kaydı koyma — silme onu da götürür.
|
||||||
|
|
||||||
### 4.2 `Wizard` bloğu — alan alan
|
### 4.2 `Wizard` bloğu — alan alan
|
||||||
|
|
||||||
|
`Wizard` bloğu **yalnızca ekranın sözleşmesinden üretilemeyen** bilgileri taşır: menü, yetki,
|
||||||
|
dil metinleri, bileşen yolu ve alan başlıklarının iki dilli karşılıkları. Ekrana ait olan her
|
||||||
|
şey (veri kaynağı, select komutu, anahtar alan, kırılım bayrakları, düzen/görünüm bayrakları,
|
||||||
|
düzenleme izinleri, görünüm option blokları, alt formlar, widget'lar, iş akışı, alan tanımları)
|
||||||
|
`ListForm`/`Fields` bölümlerindedir ve dosyaya **ikinci kez yazılmaz**.
|
||||||
|
|
||||||
|
Dosyada wizard alanları üç başlık altında toplanır — `Menu` (menü kaydı), `Language` (dil
|
||||||
|
metinleri), `Groups` (alan başlıklarının dil karşılıkları); geri kalanı kök seviyededir.
|
||||||
|
Gruplama ile `ListFormCode → WizardCode` ve `MenuParentModuleId → ModuleId` adlandırmaları
|
||||||
|
yalnızca **dosya biçimidir**; C#
|
||||||
|
sözleşmesi (`ListFormWizardDto`) düzdür ve dosya okunurken düzleştirilir
|
||||||
|
(`WizardSeedFileDto.FromSeedJson` ↔ `ToSeedJson`). Aşağıdaki tablolar C# alan adlarını kullanır.
|
||||||
|
|
||||||
|
Aşağıdaki tablolarda `[ListForm]` işaretli alanlar **istek girdisidir**: sihirbaz bunları POST
|
||||||
|
eder, sunucu ekranı bunlardan üretir, ama seed dosyasında `ListForm` bölümündeki karşılıkları
|
||||||
|
saklanır. Dosya okunurken bu alanlar `ListForm`/`Fields` üzerinden geri doldurulur
|
||||||
|
(`WizardSeedFileDto.FromSeedJson`), dolayısıyla wizard'ı okuyan kod tam nesneyi görür.
|
||||||
|
|
||||||
**Kimlik ve yol**
|
**Kimlik ve yol**
|
||||||
|
|
||||||
| Alan | Anlam |
|
| Alan | Anlam |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `ComponentKind` | `0` List (varsayılan) · `1` Custom. Dosyadaki ilk alan olmalı. |
|
| `ComponentKind` | `0` List (varsayılan) · `1` Custom. Dosyadaki ilk alan olmalı. |
|
||||||
| `WizardName` | Dosya adının ve export zip adının kaynağı. |
|
| `WizardName` | Dosya adının (`{WizardName}.json`) ve export zip adının kaynağı. |
|
||||||
| `ListFormCode` | Ekranın kodu; route ve metadata bu koda göre çözülür. |
|
| `SeededAt` | İlk deploy anı; seeder dosyaları bu damgaya göre sıralar. Sunucu üretir, düzenlemede korunur. |
|
||||||
|
| `ListFormCode` | Ekranın kodu; route, yetki adları ve dil anahtarları bu koddan türer. Dosyada `WizardCode` adıyla, üçüncü sırada yazılır. |
|
||||||
| `MenuCode` | Menü kaydının kodu; yetki adlarının kökü de budur. |
|
| `MenuCode` | Menü kaydının kodu; yetki adlarının kökü de budur. |
|
||||||
| `MenuUrl` | List'te `/admin/list/{MenuCode}` hesaplanır; Custom'da bileşenin `RoutePath`'i yazılır. |
|
| `MenuUrl` | List'te `/admin/list/{MenuCode}` hesaplanır; Custom'da bileşenin `RoutePath`'i yazılır. |
|
||||||
| `CustomComponentName` | Yalnızca Custom yolunda; bağlanacak bileşenin adı. |
|
| `CustomComponentName` | Yalnızca Custom yolunda; bağlanacak bileşenin adı. |
|
||||||
|
|
@ -600,7 +644,11 @@ Elle dosya yazarken buraya var olan bir kaydı koyma — silme onu da götürür
|
||||||
|
|
||||||
| Alan | Anlam |
|
| Alan | Anlam |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `CreateMenu` | `false` ise menü ve üst menü kaydı **hiç üretilmez**; ListForm, yetki ve dil anahtarları yine üretilir. SubGrid/parça olarak kullanılacak ekranlar için. Varsayılan `true`. |
|
> Dosyada bu bloğun tamamı `Menu` başlığı altındadır; `MenuParentModuleId` ise kök seviyede
|
||||||
|
> `ModuleId` adıyla yazılır. Menüsüz wizard'da da blok korunur: `MenuCode` yetkilerin kökü,
|
||||||
|
> Custom yolunda `MenuUrl` rotanın kaynağıdır.
|
||||||
|
|
||||||
|
| `CreateMenu` | `false` ise menü ve üst menü kaydı **hiç üretilmez**; ListForm, yetki ve dil anahtarları yine üretilir. SubGrid/parça olarak kullanılacak ekranlar için. Varsayılan `true` olduğu için dosyaya **yalnızca `false` iken** yazılır. |
|
||||||
| `MenuParentCode` / `MenuParentModuleId` / `MenuParentIcon` | Üst menü; yoksa oluşturulur. |
|
| `MenuParentCode` / `MenuParentModuleId` / `MenuParentIcon` | Üst menü; yoksa oluşturulur. |
|
||||||
| `MenuIcon` | React-icons adı (`FcBiohazard`, `FaBox`…). |
|
| `MenuIcon` | React-icons adı (`FcBiohazard`, `FaBox`…). |
|
||||||
| `MenuOrder` | Kardeşler arası sıra. |
|
| `MenuOrder` | Kardeşler arası sıra. |
|
||||||
|
|
@ -609,6 +657,9 @@ Elle dosya yazarken buraya var olan bir kaydı koyma — silme onu da götürür
|
||||||
|
|
||||||
| Alan | Anlam |
|
| Alan | Anlam |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
|
> `LanguageText*` alanlarının tamamı dosyada `Language` başlığı altındadır. `ListForm` yalnızca
|
||||||
|
> dil **anahtarını** taşır (`Title = "{ListFormCode}.Title"`), metinlerin tek kaynağı burasıdır.
|
||||||
|
|
||||||
| `PermissionGroupName` | Var olan grup ya da yeni grup adı. |
|
| `PermissionGroupName` | Var olan grup ya da yeni grup adı. |
|
||||||
| `PermissionGroupDisplayNameEn` / `Tr` | Grup adıyla aynı dil anahtarına yazılır. Var olan grup seçilip boş bırakılırsa sunucu veritabanındaki değeri geri yazar. |
|
| `PermissionGroupDisplayNameEn` / `Tr` | Grup adıyla aynı dil anahtarına yazılır. Var olan grup seçilip boş bırakılırsa sunucu veritabanındaki değeri geri yazar. |
|
||||||
| `LanguageTextMenuEn` / `Tr` | Menü etiketi. |
|
| `LanguageTextMenuEn` / `Tr` | Menü etiketi. |
|
||||||
|
|
@ -624,23 +675,23 @@ edilir.
|
||||||
|
|
||||||
| Alan | Anlam |
|
| Alan | Anlam |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `DataSourceCode` | Bağlantı kodu; varsayılan `"Default"`. |
|
| `DataSourceCode` | `[ListForm]` Bağlantı kodu; varsayılan `"Default"`. |
|
||||||
| `DataSourceConnectionString` | Yeni bir bağlantı tanımlanıyorsa; aksi hâlde boş. **Asla gerçek kimlik bilgisi yazma.** |
|
| `DataSourceConnectionString` | Yalnızca **yeni** bir bağlantı tanımlanıyorsa doldurulur; boşsa dosyaya hiç yazılmaz. **Asla gerçek kimlik bilgisi yazma.** |
|
||||||
| `SelectCommandType` | Bkz. §3. |
|
| `SelectCommandType` | `[ListForm]` Bkz. §3. |
|
||||||
| `SelectCommand` | Tablo/view adı, fonksiyon çağrısı ya da sorgu metni. |
|
| `SelectCommand` | `[ListForm]` Tablo/view adı, fonksiyon çağrısı ya da sorgu metni. |
|
||||||
| `KeyFieldName` / `KeyFieldDbSourceType` | Birincil anahtar ve tipi. |
|
| `KeyFieldName` / `KeyFieldDbSourceType` | `[ListForm]` Birincil anahtar ve tipi. |
|
||||||
|
|
||||||
**Davranış**
|
**Davranış**
|
||||||
|
|
||||||
| Alan | Anlam |
|
| Alan | Anlam |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `IsTenant` / `IsBranch` / `IsOrganizationUnit` | Otomatik kırılım filtresi. Tabloda `TenantId` varsa `IsTenant: true` **olmalı**. |
|
| `IsTenant` / `IsBranch` / `IsOrganizationUnit` | `[ListForm]` Otomatik kırılım filtresi. Tabloda `TenantId` varsa `IsTenant: true` **olmalı**. |
|
||||||
| `AllowAdding` / `AllowUpdating` / `AllowDeleting` / `ConfirmDelete` | Toolbar ve satır aksiyonları. |
|
| `AllowAdding` / `AllowUpdating` / `AllowDeleting` / `ConfirmDelete` | `[ListForm]` Toolbar ve satır aksiyonları (`EditingOptionDto`). |
|
||||||
| `AllowDetail` | Satırda **Detay** düğmesi çıkar; `/admin/form/{ListFormCode}/{anahtar}` adresini yeni sekmede açar — aynı ekranın tek kayıtlık form görünümüdür. `KeyFieldName` boşsa çalışmaz. |
|
| `AllowDetail` | `[ListForm]` Satırda **Detay** düğmesi çıkar; `/admin/form/{ListFormCode}/{anahtar}` adresini yeni sekmede açar — aynı ekranın tek kayıtlık form görünümüdür. `KeyFieldName` boşsa çalışmaz. |
|
||||||
| `DefaultLayout` | `"grid"`, `"card"`, `"pivot"`, `"chart"`, `"tree"`, `"gantt"`, `"scheduler"`, `"todo"`. |
|
| `DefaultLayout` | `[ListForm]` `"grid"`, `"card"`, `"pivot"`, `"chart"`, `"tree"`, `"gantt"`, `"scheduler"`, `"todo"`. |
|
||||||
| `Grid` `Card` `Pivot` `Chart` `Tree` `Gantt` `Scheduler` `Todo` | Hangi görünümlerin açık olduğu. Açtığın her görünümün option bloğunu da doldur. |
|
| `Grid` `Card` `Pivot` `Chart` `Tree` `Gantt` `Scheduler` `Todo` | `[ListForm]` Hangi görünümlerin açık olduğu (`LayoutDto`). Açtığın her görünümün option bloğunu da doldur. |
|
||||||
|
|
||||||
**Görünüm option blokları** — yalnızca ilgili bayrak `true` ise anlamlıdır:
|
**Görünüm option blokları** — `[ListForm]`; yalnızca ilgili bayrak `true` ise anlamlıdır:
|
||||||
|
|
||||||
| Blok | Zorunlu alanlar |
|
| Blok | Zorunlu alanlar |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
|
|
@ -666,6 +717,11 @@ kaynağı odur. Grup yapısı yalnızca düzenleme formunun yerleşimini belirle
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Seed dosyasında `Groups` **kırpılmış** hâlde durur: grup başlığı/kolon sayısı ve alanların
|
||||||
|
editör bilgileri `ListForm.EditingFormDto` ve `Fields` içinde olduğu için tekrarlanmaz; dosyada
|
||||||
|
her öğe yalnızca `FieldName`, `CaptionName` ve `TurkishCaption`/`EnglishCaption` taşır — çünkü
|
||||||
|
alan başlıklarının dil metinlerinin başka kaynağı yoktur. Okurken tümü geri doldurulur.
|
||||||
|
|
||||||
Her `Items` öğesi:
|
Her `Items` öğesi:
|
||||||
|
|
||||||
| Alan | Anlam |
|
| Alan | Anlam |
|
||||||
|
|
@ -673,16 +729,16 @@ Her `Items` öğesi:
|
||||||
| `FieldName` | Veri kolonunun adı (SQL'deki adıyla birebir). |
|
| `FieldName` | Veri kolonunun adı (SQL'deki adıyla birebir). |
|
||||||
| `CaptionName` | Dil anahtarı ya da düz başlık. |
|
| `CaptionName` | Dil anahtarı ya da düz başlık. |
|
||||||
| `TurkishCaption` / `EnglishCaption` | Dil metinleri; ikisi de doldurulur. |
|
| `TurkishCaption` / `EnglishCaption` | Dil metinleri; ikisi de doldurulur. |
|
||||||
| `EditorType` | Bkz. §3. |
|
| `EditorType` | `[Fields]` Bkz. §3. |
|
||||||
| `EditorOptions` | DevExtreme editör JSON'u — §6. |
|
| `EditorOptions` | `[Fields]` DevExtreme editör JSON'u — §6. |
|
||||||
| `EditorScript` | Alan davranış script'i — §5. |
|
| `EditorScript` | `[Fields]` Alan davranış script'i — §5. |
|
||||||
| `DbSourceType` | Bkz. §3. |
|
| `DbSourceType` | `[Fields]` Bkz. §3. |
|
||||||
| `IsRequired` | Zorunluluk. |
|
| `IsRequired` | `[Fields]` Zorunluluk (`ValidationRuleDto`). |
|
||||||
| `IncludeInEditingForm` | `false` ise yalnızca grid sütunu olur, forma girmez. |
|
| `IncludeInEditingForm` | `[Fields]` `false` ise yalnızca grid sütunu olur, forma girmez (`EditGroupOrderNo`). |
|
||||||
| `ColSpan` | Formda kaç kolon kaplar (grubun `ColCount` değeri içinde). |
|
| `ColSpan` | `[Fields]` Formda kaç kolon kaplar (grubun `ColCount` değeri içinde). |
|
||||||
| `LookupDataSourceType` | `1/2/3` — bkz. §3. `0` = lookup yok. |
|
| `LookupDataSourceType` | `[Fields]` `1/2/3` — bkz. §3. `0` = lookup yok. |
|
||||||
| `LookupQuery` | `Query` tipinde SQL; `StaticData` tipinde JSON dizisi; `WebService` tipinde URL. |
|
| `LookupQuery` | `[Fields]` `Query` tipinde SQL; `StaticData` tipinde JSON dizisi; `WebService` tipinde URL. |
|
||||||
| `ValueExpr` / `DisplayExpr` | Lookup'ın değer ve etiket kolonları. |
|
| `ValueExpr` / `DisplayExpr` | `[Fields]` Lookup'ın değer ve etiket kolonları. |
|
||||||
|
|
||||||
### 4.3.1 Sütunlar nasıl otomatik üretilir
|
### 4.3.1 Sütunlar nasıl otomatik üretilir
|
||||||
|
|
||||||
|
|
@ -990,6 +1046,7 @@ varsayılan değeri olur ve **yeni kayıtta o alanı otomatik doldurur**.
|
||||||
"Wizard": {
|
"Wizard": {
|
||||||
"ComponentKind": 0,
|
"ComponentKind": 0,
|
||||||
"WizardName": "Orders",
|
"WizardName": "Orders",
|
||||||
|
"SeededAt": "2026-08-26T10:15:00",
|
||||||
"ListFormCode": "App.Mrp.Orders",
|
"ListFormCode": "App.Mrp.Orders",
|
||||||
"MenuCode": "App.Mrp.Orders",
|
"MenuCode": "App.Mrp.Orders",
|
||||||
"MenuOrder": 1,
|
"MenuOrder": 1,
|
||||||
|
|
@ -1212,7 +1269,7 @@ widget, sorgunun döndürdüğü **her satır için bir kart** çizer:
|
||||||
Ayrıntı: `lowcode-reference.instructions.md` §5.3.
|
Ayrıntı: `lowcode-reference.instructions.md` §5.3.
|
||||||
|
|
||||||
Alt form olarak kullanılacak ekranı **menüsüz wizard** (`CreateMenu: false`) ile üret; menüde
|
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. Dosya adının zaman damgası ana
|
görünmesin ama kendi yetkileri ve alan tanımları olsun. `Wizard.SeededAt` damgası ana
|
||||||
wizard'ınkinden **küçük** olmalıdır (§0.2).
|
wizard'ınkinden **küçük** olmalıdır (§0.2).
|
||||||
|
|
||||||
### 4.7 İş akışı (onay süreci)
|
### 4.7 İş akışı (onay süreci)
|
||||||
|
|
@ -2311,7 +2368,7 @@ Bu çift, custom component'in menüye nasıl bağlandığının çalışan örne
|
||||||
```
|
```
|
||||||
configs/seeds/host/custom/RoleComponent.json
|
configs/seeds/host/custom/RoleComponent.json
|
||||||
configs/seeds/host/crud/AbpRoles.json · AbpUsers.json · AbpUserRoles.json
|
configs/seeds/host/crud/AbpRoles.json · AbpUsers.json · AbpUserRoles.json
|
||||||
configs/seeds/host/wizard/20260819131500_RoleList.json
|
configs/seeds/host/wizard/RoleList.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Nasıl bağlanıyorlar:
|
Nasıl bağlanıyorlar:
|
||||||
|
|
@ -3419,8 +3476,8 @@ configs/seeds/host/
|
||||||
├── sql/object/Hr_T_AdvanceRequest.sql ← tablo (+ PostgreSQL hedefse postgres/object/…)
|
├── sql/object/Hr_T_AdvanceRequest.sql ← tablo (+ PostgreSQL hedefse postgres/object/…)
|
||||||
├── crud/Hr_T_AdvanceRequest.json ← yalnızca Custom Component yolunda gerekir
|
├── crud/Hr_T_AdvanceRequest.json ← yalnızca Custom Component yolunda gerekir
|
||||||
├── custom/AdvanceRequestForm.json ← yalnızca serbest yerleşim isteniyorsa
|
├── custom/AdvanceRequestForm.json ← yalnızca serbest yerleşim isteniyorsa
|
||||||
├── wizard/20260826101000_AdvanceRequestApprovals.json ← (varsa) menüsüz alt ekran, önce
|
├── wizard/AdvanceRequestApprovals.json ← (varsa) menüsüz alt ekran, küçük SeededAt
|
||||||
└── wizard/20260826101500_AdvanceRequests.json ← ana ekran + menü + yetki + dil + onay akışı
|
└── wizard/AdvanceRequests.json ← ana ekran + menü + yetki + dil + onay akışı
|
||||||
```
|
```
|
||||||
|
|
||||||
**Karar:** Talep formu **standart bir kayıt listesi + düzenleme formu + onay akışı** ise
|
**Karar:** Talep formu **standart bir kayıt listesi + düzenleme formu + onay akışı** ise
|
||||||
|
|
@ -3483,7 +3540,7 @@ tedarikçi lookup'ı + iki kademeli onay).
|
||||||
Prompt ile üretim yaptıysan ek olarak:
|
Prompt ile üretim yaptıysan ek olarak:
|
||||||
|
|
||||||
- [ ] Dosyalar doğru kapsam klasöründe (`host/` ya da `tenants/{tenantId}/`).
|
- [ ] Dosyalar doğru kapsam klasöründe (`host/` ya da `tenants/{tenantId}/`).
|
||||||
- [ ] Üretim sırası doğru (§0.2) ve bağımlı wizard'ın zaman damgası daha büyük.
|
- [ ] Üretim sırası doğru (§0.2) ve bağımlı wizard'ın `SeededAt` damgası daha büyük.
|
||||||
- [ ] Tablo/kolon adları uydurulmadı; ya var olan nesneden okundu ya da `sql/object/` altında
|
- [ ] Tablo/kolon adları uydurulmadı; ya var olan nesneden okundu ya da `sql/object/` altında
|
||||||
üretildi.
|
üretildi.
|
||||||
- [ ] `KeyFieldDbSourceType`, tablodaki `Id` tipiyle uyumlu.
|
- [ ] `KeyFieldDbSourceType`, tablodaki `Id` tipiyle uyumlu.
|
||||||
|
|
|
||||||
18
README.md
18
README.md
|
|
@ -487,7 +487,7 @@ butonlar gösterilebilir.
|
||||||
5. **Permission** kayıtları (`.Default`, `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note`)
|
5. **Permission** kayıtları (`.Default`, `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note`)
|
||||||
6. **Dil anahtarları** (menü, başlık, açıklama; EN + TR)
|
6. **Dil anahtarları** (menü, başlık, açıklama; EN + TR)
|
||||||
7. Gerekiyorsa **ayar/entegrasyon** bağımlılıkları
|
7. Gerekiyorsa **ayar/entegrasyon** bağımlılıkları
|
||||||
8. **Seed dosyası** — `configs/seeds/{host|tenants/{tenantId}}/wizard/{zaman damgası}_{Ad}.json`
|
8. **Seed dosyası** — `configs/seeds/{host|tenants/{tenantId}}/wizard/{Ad}.json` (wizard cevapları + üretilen `ListForm`/`Fields` sözleşmesi)
|
||||||
9. **Doğrulama ve geri alma** notları
|
9. **Doğrulama ve geri alma** notları
|
||||||
|
|
||||||
**Adım bileşenleri.** Adımlar numaralı dosya adları yerine işlevleriyle adlandırılır ve
|
**Adım bileşenleri.** Adımlar numaralı dosya adları yerine işlevleriyle adlandırılır ve
|
||||||
|
|
@ -509,6 +509,16 @@ adım sayısından bağımsızdır.
|
||||||
Menü ağacı yardımcıları (`menuTree.ts`) adım bileşenlerinden ayrı bir modüldedir; böylece Wizard
|
Menü ağacı yardımcıları (`menuTree.ts`) adım bileşenlerinden ayrı bir modüldedir; böylece Wizard
|
||||||
ve `SqlTableDesignerDialog` bu fonksiyonları kullanırken adım bileşenlerini pakete çekmez.
|
ve `SqlTableDesignerDialog` bu fonksiyonları kullanırken adım bileşenlerini pakete çekmez.
|
||||||
|
|
||||||
|
**Seed dosyası ekranın kendisini taşır.** Dosya `Wizard` bölümünün yanında ekranın
|
||||||
|
`GridOptionsEditDto` sözleşmesini (`ListForm`) ve alanların `ColumnFormatEditDto` sözleşmesini
|
||||||
|
(`Fields`) tutar; `WizardDataSeeder` ekranı bu iki bölümden kurar. Böylece Wizard ile ListForm
|
||||||
|
detayı aynı veri yapısını konuşur ve tasarımcıdaki değişiklikler wizard'a geri yazılabilir.
|
||||||
|
`Wizard` bölümü yalnızca ekrandan üretilemeyen bilgileri (menü, yetki, dil metinleri, bileşen
|
||||||
|
yolu, alan başlıklarının dil karşılıkları) taşır; `ListForm`/`Fields` içinde karşılığı olan alanlar
|
||||||
|
dosyaya yazılmaz, okuma sırasında geri doldurulur. Kalanlar dosyada `Menu`, `Language` ve `Groups`
|
||||||
|
başlıkları altında toplanır (`WizardSeedFileDto.ToSeedJson` / `FromSeedJson`). Dosya adı `{Ad}.json`'dır (zaman damgası yok);
|
||||||
|
uygulama sırası `Wizard.SeededAt` damgasından gelir.
|
||||||
|
|
||||||
**İki yol: `ComponentKind`.** `ListFormWizardDto.ComponentKind` sihirbazın yol ayrımıdır ve seed
|
**İki yol: `ComponentKind`.** `ListFormWizardDto.ComponentKind` sihirbazın yol ayrımıdır ve seed
|
||||||
dosyasında ilk alandır. Varsayılan `List` olduğu için bu alanı taşımayan eski seed dosyaları
|
dosyasında ilk alandır. Varsayılan `List` olduğu için bu alanı taşımayan eski seed dosyaları
|
||||||
eskisi gibi çalışır.
|
eskisi gibi çalışır.
|
||||||
|
|
@ -1165,7 +1175,7 @@ geri yüklenir. Kapsam klasörü CDN düzeniyle aynıdır ve `SeedPathResolver`
|
||||||
```
|
```
|
||||||
configs/seeds/
|
configs/seeds/
|
||||||
├── host/ # host kapsamı
|
├── host/ # host kapsamı
|
||||||
│ ├── wizard/ # {zaman damgası}_{Ad}.json → WizardDataSeeder
|
│ ├── wizard/ # {Ad}.json → WizardDataSeeder
|
||||||
│ ├── custom/ # {ComponentName}.json → CustomComponentDataSeeder
|
│ ├── custom/ # {ComponentName}.json → CustomComponentDataSeeder
|
||||||
│ ├── crud/ # {EntityName}.json → CrudDataSeeder
|
│ ├── crud/ # {EntityName}.json → CrudDataSeeder
|
||||||
│ ├── sql/{object,execute}/ # .sql (SQL Server)
|
│ ├── sql/{object,execute}/ # .sql (SQL Server)
|
||||||
|
|
@ -1176,7 +1186,9 @@ configs/seeds/
|
||||||
|
|
||||||
> Bu dosyalar elle de düzenlenebilir; ilgili ekrandan tekrar kaydedildiğinde yeniden üretilirler.
|
> Bu dosyalar elle de düzenlenebilir; ilgili ekrandan tekrar kaydedildiğinde yeniden üretilirler.
|
||||||
> Wizard dosyası `EditFileName` ile güncellenirse sunucu önce eski dosyayı ve ürettiği kayıtları
|
> Wizard dosyası `EditFileName` ile güncellenirse sunucu önce eski dosyayı ve ürettiği kayıtları
|
||||||
> siler, sonra yenisini üretir.
|
> siler, sonra yenisini üretir. Wizard'ın ürettiği ekran **ListForm detayından** düzenlendiğinde
|
||||||
|
> de aynı dosya güncellenir (`WizardSeedSynchronizer`): `ListForm`/`Fields` bölümleri ve
|
||||||
|
> `Wizard` bölümündeki karşılıkları veritabanındaki güncel haliyle değiştirilir.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,5 +23,51 @@ public class ColumnFormatEditDto : ColumnFormatDto
|
||||||
}
|
}
|
||||||
set { JoinTableJson = JsonSerializer.Serialize(value); }
|
set { JoinTableJson = JsonSerializer.Serialize(value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>ListFormField sozlesmesini kayit nesnesine yazar.</summary>
|
||||||
|
public void ApplyTo(IListFormFieldArtifact target)
|
||||||
|
{
|
||||||
|
target.ListFormCode = ListFormCode;
|
||||||
|
target.UserId = UserId;
|
||||||
|
target.RoleId = RoleId;
|
||||||
|
target.CultureName = CultureName;
|
||||||
|
target.FieldName = FieldName;
|
||||||
|
target.CaptionName = CaptionName;
|
||||||
|
target.PlaceHolder = PlaceHolder;
|
||||||
|
target.BandName = BandName;
|
||||||
|
target.Visible = Visible;
|
||||||
|
target.IsActive = IsActive;
|
||||||
|
target.Width = Width;
|
||||||
|
target.ListOrderNo = ListOrderNo;
|
||||||
|
target.SourceDbType = SourceDbType;
|
||||||
|
target.SortIndex = SortIndex;
|
||||||
|
target.SortDirection = SortDirection;
|
||||||
|
target.AllowSearch = AllowSearch;
|
||||||
|
target.AllowEditing = AllowEditing;
|
||||||
|
target.AllowAdding = AllowAdding;
|
||||||
|
target.Alignment = Alignment;
|
||||||
|
target.Format = Format;
|
||||||
|
target.EditOrderNo = EditOrderNo;
|
||||||
|
target.EditGroupOrderNo = EditGroupOrderNo;
|
||||||
|
target.EditorType2 = EditorType2;
|
||||||
|
target.ColSpan = ColSpan;
|
||||||
|
target.EditorOptions = EditorOptions;
|
||||||
|
target.EditorScript = EditorScript;
|
||||||
|
target.ColumnCssClass = ColumnCssClass;
|
||||||
|
target.ColumnCssValue = ColumnCssValue;
|
||||||
|
|
||||||
|
target.ColumnFilterJson = ColumnFilterJson;
|
||||||
|
target.ColumnHeaderJson = ColumnHeaderJson;
|
||||||
|
target.GroupingJson = GroupingJson;
|
||||||
|
target.ColumnCustomizationJson = ColumnCustomizationJson;
|
||||||
|
target.TotalSummaryJson = TotalSummaryJson;
|
||||||
|
target.GroupSummaryJson = GroupSummaryJson;
|
||||||
|
target.JoinTableJson = JoinTableJson;
|
||||||
|
target.LookupJson = LookupJson;
|
||||||
|
target.ValidationRuleJson = ValidationRuleJson;
|
||||||
|
target.ColumnStylingJson = ColumnStylingJson;
|
||||||
|
target.PermissionJson = PermissionJson;
|
||||||
|
target.PivotSettingsJson = PivotSettingsJson;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,5 +123,108 @@ public class GridOptionsEditDto : GridOptionsDto
|
||||||
}
|
}
|
||||||
set { ExtraFilterJson = JsonSerializer.Serialize(value); }
|
set { ExtraFilterJson = JsonSerializer.Serialize(value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ListForm sozlesmesini kayit nesnesine yazar. Seed dosyasindan okunan ekran ile tasarimcinin
|
||||||
|
/// urettigi ekranin ayni kolonlari doldurmasi bu tek noktadan garanti edilir.
|
||||||
|
/// </summary>
|
||||||
|
public void ApplyTo(IListFormArtifact target)
|
||||||
|
{
|
||||||
|
target.ListFormCode = ListFormCode;
|
||||||
|
target.CultureName = CultureName;
|
||||||
|
target.Name = Name;
|
||||||
|
target.Title = Title;
|
||||||
|
target.Description = Description;
|
||||||
|
target.ListFormType = ListFormType;
|
||||||
|
target.IsSubForm = IsSubForm;
|
||||||
|
target.SubFormsListFormType = SubFormsListFormType;
|
||||||
|
target.ShowNote = ShowNote;
|
||||||
|
target.SortMode = SortMode;
|
||||||
|
target.PageSize = PageSize;
|
||||||
|
target.Width = Width;
|
||||||
|
target.Height = Height;
|
||||||
|
target.FullHeight = FullHeight;
|
||||||
|
target.UserId = UserId;
|
||||||
|
target.RoleId = RoleId;
|
||||||
|
|
||||||
|
target.DataSourceCode = DataSourceCode;
|
||||||
|
target.SelectCommandType = SelectCommandType;
|
||||||
|
target.SelectCommand = SelectCommand;
|
||||||
|
target.TableName = TableName;
|
||||||
|
target.KeyFieldName = KeyFieldName;
|
||||||
|
target.KeyFieldDbSourceType = KeyFieldDbSourceType;
|
||||||
|
target.IsTenant = IsTenant;
|
||||||
|
target.IsBranch = IsBranch;
|
||||||
|
target.IsOrganizationUnit = IsOrganizationUnit;
|
||||||
|
target.DefaultFilter = DefaultFilter;
|
||||||
|
|
||||||
|
target.InsertServiceAddress = InsertServiceAddress;
|
||||||
|
target.InsertCommand = InsertCommand;
|
||||||
|
target.InsertBeforeCommand = InsertBeforeCommand;
|
||||||
|
target.InsertAfterCommand = InsertAfterCommand;
|
||||||
|
target.UpdateServiceAddress = UpdateServiceAddress;
|
||||||
|
target.UpdateCommand = UpdateCommand;
|
||||||
|
target.UpdateBeforeCommand = UpdateBeforeCommand;
|
||||||
|
target.UpdateAfterCommand = UpdateAfterCommand;
|
||||||
|
target.DeleteServiceAddress = DeleteServiceAddress;
|
||||||
|
target.DeleteCommand = DeleteCommand;
|
||||||
|
target.DeleteBeforeCommand = DeleteBeforeCommand;
|
||||||
|
target.DeleteAfterCommand = DeleteAfterCommand;
|
||||||
|
|
||||||
|
target.SelectFieldsDefaultValueJson = SelectFieldsDefaultValueJson;
|
||||||
|
target.InsertFieldsDefaultValueJson = InsertFieldsDefaultValueJson;
|
||||||
|
target.UpdateFieldsDefaultValueJson = UpdateFieldsDefaultValueJson;
|
||||||
|
target.DeleteFieldsDefaultValueJson = DeleteFieldsDefaultValueJson;
|
||||||
|
target.FormFieldsDefaultValueJson = FormFieldsDefaultValueJson;
|
||||||
|
|
||||||
|
target.LayoutJson = LayoutJson;
|
||||||
|
target.FilterRowJson = FilterRowJson;
|
||||||
|
target.RowJson = RowJson;
|
||||||
|
target.HeaderFilterJson = HeaderFilterJson;
|
||||||
|
target.FilterPanelJson = FilterPanelJson;
|
||||||
|
target.SearchPanelJson = SearchPanelJson;
|
||||||
|
target.GroupPanelJson = GroupPanelJson;
|
||||||
|
target.SelectionJson = SelectionJson;
|
||||||
|
target.ColumnOptionJson = ColumnOptionJson;
|
||||||
|
target.PivotOptionJson = PivotOptionJson;
|
||||||
|
target.TreeOptionJson = TreeOptionJson;
|
||||||
|
target.GanttOptionJson = GanttOptionJson;
|
||||||
|
target.SchedulerOptionJson = SchedulerOptionJson;
|
||||||
|
target.TodoOptionJson = TodoOptionJson;
|
||||||
|
target.PagerOptionJson = PagerOptionJson;
|
||||||
|
target.EditingOptionJson = EditingOptionJson;
|
||||||
|
target.EditingFormJson = EditingFormJson;
|
||||||
|
target.PermissionJson = PermissionJson;
|
||||||
|
target.CommandColumnJson = CommandColumnJson;
|
||||||
|
target.StateStoringJson = StateStoringJson;
|
||||||
|
target.CustomJsSourcesJson = CustomJsSourcesJson;
|
||||||
|
target.CustomStyleSourcesJson = CustomStyleSourcesJson;
|
||||||
|
target.SubFormsJson = SubFormsJson;
|
||||||
|
target.WidgetsJson = WidgetsJson;
|
||||||
|
target.WorkflowJson = WorkflowJson;
|
||||||
|
target.ExtraFilterJson = ExtraFilterJson;
|
||||||
|
|
||||||
|
target.CommonJson = CommonJson;
|
||||||
|
target.AdaptiveLayoutJson = AdaptiveLayoutJson;
|
||||||
|
target.AnimationJson = AnimationJson;
|
||||||
|
target.AnnotationsJson = AnnotationsJson;
|
||||||
|
target.ArgumentAxisJson = ArgumentAxisJson;
|
||||||
|
target.CommonAnnotationsSettingsJson = CommonAnnotationsSettingsJson;
|
||||||
|
target.CommonAxisSettingsJson = CommonAxisSettingsJson;
|
||||||
|
target.CommonPaneSettingsJson = CommonPaneSettingsJson;
|
||||||
|
target.CommonSeriesSettingsJson = CommonSeriesSettingsJson;
|
||||||
|
target.CrosshairJson = CrosshairJson;
|
||||||
|
target.ExportJson = ExportJson;
|
||||||
|
target.LegendJson = LegendJson;
|
||||||
|
target.MarginJson = MarginJson;
|
||||||
|
target.PanesJson = PanesJson;
|
||||||
|
target.ScrollBarJson = ScrollBarJson;
|
||||||
|
target.SeriesJson = SeriesJson;
|
||||||
|
target.SizeJson = SizeJson;
|
||||||
|
target.TitleJson = TitleJson;
|
||||||
|
target.TooltipJson = TooltipJson;
|
||||||
|
target.ValueAxisJson = ValueAxisJson;
|
||||||
|
target.ZoomAndPanJson = ZoomAndPanJson;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,25 @@
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
using Sozsoft.Languages.Languages;
|
||||||
using Sozsoft.Platform.Enums;
|
using Sozsoft.Platform.Enums;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.ListForms;
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wizard adimlarinin cevaplari ve bu cevaplardan ekran sozlesmesini ureten donusumler.
|
||||||
|
/// <para>
|
||||||
|
/// Deploy (<c>ListFormWizardAppService</c>), seed (<c>WizardDataSeeder</c>) ve tasarimci
|
||||||
|
/// senkronizasyonu (<c>WizardSeedSynchronizer</c>) ayni donusumleri buradan kullanir; boylece
|
||||||
|
/// uc yolun ciktisi birbirinden ayrisamaz. Entity'ye yazma islemi
|
||||||
|
/// <see cref="GridOptionsEditDto.ApplyTo"/> ve <see cref="ColumnFormatEditDto.ApplyTo"/>
|
||||||
|
/// uzerinden, Domain.Shared'deki kayit sozlesmeleriyle yapilir; boylece entity katmanini
|
||||||
|
/// tanimayan DbMigrator da ayni donusumu kullanabilir.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
public class ListFormWizardDto
|
public class ListFormWizardDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -23,6 +38,13 @@ public class ListFormWizardDto
|
||||||
public string EditFileName { get; set; }
|
public string EditFileName { get; set; }
|
||||||
|
|
||||||
public string WizardName { get; set; }
|
public string WizardName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wizard'in ilk deploy edildigi an. Dosya adi tarih tasimadigi icin seeder dosyalari bu
|
||||||
|
/// damgaya gore siralar; birbirine bagli wizard'lar (once Customers, sonra Tasks) uretildikleri
|
||||||
|
/// sirayla uygulanir. Duzenlemede ilk deger korunur.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime SeededAt { get; set; }
|
||||||
public string ListFormCode { get; set; }
|
public string ListFormCode { get; set; }
|
||||||
public string MenuCode { get; set; }
|
public string MenuCode { get; set; }
|
||||||
public int MenuOrder { get; set; }
|
public int MenuOrder { get; set; }
|
||||||
|
|
@ -107,4 +129,450 @@ public class ListFormWizardDto
|
||||||
public List<WizardColumnGroupInputDto> Groups { get; set; } = new();
|
public List<WizardColumnGroupInputDto> Groups { get; set; } = new();
|
||||||
public List<SubFormDto> SubForms { get; set; } = new();
|
public List<SubFormDto> SubForms { get; set; } = new();
|
||||||
public List<WidgetEditDto> Widgets { get; set; } = new();
|
public List<WidgetEditDto> Widgets { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>Wizard'in menu/permission kodu; ad ve kod bos gelebildigi icin tek kuraldan cozulur.</summary>
|
||||||
|
public string ResolveMenuCode()
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(MenuCode)
|
||||||
|
? WizardConsts.WizardKey(WizardName?.Trim())
|
||||||
|
: MenuCode.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Wizard'in ListForm kodu; bos gelirse menu kodu ile aynidir.</summary>
|
||||||
|
public string ResolveListFormCode()
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(ListFormCode)
|
||||||
|
? ResolveMenuCode()
|
||||||
|
: ListFormCode.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wizard girdisinden ListForm sozlesmesini uretir. <paramref name="isDeletedField"/> ve
|
||||||
|
/// <paramref name="isCreatedField"/> hedef tablonun soft delete / audit kolonlarini tasiyip
|
||||||
|
/// tasimadigini bildirir; silme komutu ve varsayilan alan degerleri buna gore kurulur.
|
||||||
|
/// </summary>
|
||||||
|
public GridOptionsEditDto BuildListForm(bool isDeletedField, bool isCreatedField)
|
||||||
|
{
|
||||||
|
var code = ResolveMenuCode();
|
||||||
|
var listFormCode = ResolveListFormCode();
|
||||||
|
var titleLangKey = $"{listFormCode}.Title";
|
||||||
|
var descLangKey = $"{listFormCode}.Desc";
|
||||||
|
|
||||||
|
var workflow = NormalizeWorkflow(WorkflowDto, listFormCode);
|
||||||
|
WorkflowDto = workflow;
|
||||||
|
|
||||||
|
var groups = Groups ?? [];
|
||||||
|
var subForms = SubForms ?? [];
|
||||||
|
var widgets = Widgets ?? [];
|
||||||
|
|
||||||
|
var editingFormDtos = groups
|
||||||
|
.Select((g, gi) => new { Group = g, Order = gi + 1 })
|
||||||
|
.Where(x => x.Group.Items.Any(i => i.IncludeInEditingForm && i.FieldName != KeyFieldName))
|
||||||
|
.Select(x => new EditingFormDto
|
||||||
|
{
|
||||||
|
Order = x.Order,
|
||||||
|
Caption = x.Group.Caption,
|
||||||
|
ColCount = x.Group.ColCount,
|
||||||
|
ColSpan = x.Group.ColCount,
|
||||||
|
ItemType = "group"
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var (formWidth, formHeight) = CalculateEditFormSize();
|
||||||
|
|
||||||
|
var dto = new GridOptionsEditDto
|
||||||
|
{
|
||||||
|
ListFormCode = listFormCode,
|
||||||
|
CultureName = LanguageCodes.En,
|
||||||
|
ListFormType = ListFormTypeEnum.List,
|
||||||
|
Name = code,
|
||||||
|
Title = titleLangKey,
|
||||||
|
Description = descLangKey,
|
||||||
|
PageSize = 10,
|
||||||
|
IsSubForm = false,
|
||||||
|
ShowNote = subForms.Count > 0 || workflow.Criteria.Count > 0,
|
||||||
|
SortMode = PlatformConsts.GridOptions.SortModeSingle,
|
||||||
|
DataSourceCode = DataSourceCode,
|
||||||
|
IsTenant = IsTenant,
|
||||||
|
IsBranch = IsBranch,
|
||||||
|
IsOrganizationUnit = IsOrganizationUnit,
|
||||||
|
SelectCommandType = SelectCommandType,
|
||||||
|
SelectCommand = SelectCommand,
|
||||||
|
KeyFieldName = KeyFieldName,
|
||||||
|
KeyFieldDbSourceType = KeyFieldDbSourceType,
|
||||||
|
DefaultFilter = isDeletedField ? WizardConsts.DefaultFilterJson : null,
|
||||||
|
DeleteCommand = isDeletedField ? WizardConsts.DefaultDeleteCommand(SelectCommand) : null,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Varsayilanlar WizardConsts uzerinden JSON olarak kurulur; DTO'nun nesne karsiliklari
|
||||||
|
// (FilterRowDto, EditingOptionDto ...) bu JSON'lardan turedigi icin seed dosyasi ayni
|
||||||
|
// degerleri nesne olarak tasir.
|
||||||
|
dto.ExportJson = WizardConsts.DefaultExportJson;
|
||||||
|
dto.LayoutJson = WizardConsts.DefaultLayoutJson(
|
||||||
|
DefaultLayout: DefaultLayout,
|
||||||
|
Grid: Grid,
|
||||||
|
Card: Card,
|
||||||
|
Pivot: Pivot,
|
||||||
|
Chart: Chart,
|
||||||
|
Tree: Tree,
|
||||||
|
Gantt: Gantt,
|
||||||
|
Scheduler: Scheduler,
|
||||||
|
Todo: Todo);
|
||||||
|
dto.FilterRowJson = WizardConsts.DefaultFilterRowJson();
|
||||||
|
dto.HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson();
|
||||||
|
dto.SearchPanelJson = WizardConsts.DefaultSearchPanelJson();
|
||||||
|
dto.GroupPanelJson = WizardConsts.DefaultGroupPanelJson();
|
||||||
|
dto.SelectionJson = WizardConsts.DefaultSelectionSingleJson(workflow.Criteria.Count > 0
|
||||||
|
? PlatformConsts.GridOptions.SelectionModeSingle
|
||||||
|
: PlatformConsts.GridOptions.SelectionModeNone);
|
||||||
|
dto.ColumnOptionJson = WizardConsts.DefaultColumnOptionJson();
|
||||||
|
dto.PermissionJson = WizardConsts.DefaultPermissionJson(code);
|
||||||
|
dto.PagerOptionJson = WizardConsts.DefaultPagerOptionJson;
|
||||||
|
dto.EditingOptionJson = WizardConsts.DefaultEditingOptionJson(
|
||||||
|
titleLangKey, formWidth, formHeight,
|
||||||
|
AllowDeleting, AllowAdding, AllowUpdating,
|
||||||
|
ConfirmDelete, false, AllowDetail);
|
||||||
|
dto.DeleteFieldsDefaultValueJson = isDeletedField
|
||||||
|
? WizardConsts.DefaultDeleteFieldsDefaultValueJson(KeyFieldDbSourceType)
|
||||||
|
: WizardConsts.DefaultDeleteFieldsJsonOnlyId(KeyFieldDbSourceType);
|
||||||
|
dto.InsertFieldsDefaultValueJson = isCreatedField
|
||||||
|
? WizardConsts.DefaultInsertFieldsDefaultValueJson(KeyFieldDbSourceType)
|
||||||
|
: WizardConsts.DefaultInsertFieldsJsonOnlyId(KeyFieldDbSourceType);
|
||||||
|
dto.EditingFormJson = editingFormDtos.Count > 0 ? JsonSerializer.Serialize(editingFormDtos) : null;
|
||||||
|
dto.SubFormsJson = subForms.Count > 0 ? JsonSerializer.Serialize(subForms) : null;
|
||||||
|
dto.WidgetsJson = widgets.Count > 0 ? JsonSerializer.Serialize(widgets) : null;
|
||||||
|
dto.WorkflowJson = HasWorkflow(workflow) ? JsonSerializer.Serialize(workflow) : null;
|
||||||
|
dto.TreeOptionJson = UsesLayout("tree", Tree) &&
|
||||||
|
!string.IsNullOrEmpty(TreeOptionDto?.ParentIdExpr)
|
||||||
|
? JsonSerializer.Serialize(TreeOptionDto)
|
||||||
|
: null;
|
||||||
|
dto.GanttOptionJson = UsesLayout("gantt", Gantt) &&
|
||||||
|
!string.IsNullOrEmpty(GanttOptionDto?.ParentIdExpr)
|
||||||
|
? JsonSerializer.Serialize(GanttOptionDto)
|
||||||
|
: null;
|
||||||
|
dto.SchedulerOptionJson = UsesLayout("scheduler", Scheduler) &&
|
||||||
|
!string.IsNullOrEmpty(SchedulerOptionDto?.TextExpr)
|
||||||
|
? JsonSerializer.Serialize(SchedulerOptionDto)
|
||||||
|
: null;
|
||||||
|
dto.TodoOptionJson = UsesLayout("todo", Todo) &&
|
||||||
|
!string.IsNullOrEmpty(TodoOptionDto?.TitleExpr) &&
|
||||||
|
!string.IsNullOrEmpty(TodoOptionDto?.StatusExpr)
|
||||||
|
? JsonSerializer.Serialize(TodoOptionDto)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wizard gruplarindaki her alani ListFormField sozlesmesine cevirir. Alan sirasi gruplarin
|
||||||
|
/// sirasini izler; anahtar alan listede gizlenir ve duzenleme formuna alinmaz.
|
||||||
|
/// </summary>
|
||||||
|
public List<ColumnFormatEditDto> BuildFields()
|
||||||
|
{
|
||||||
|
var code = ResolveMenuCode();
|
||||||
|
var listFormCode = ResolveListFormCode();
|
||||||
|
var fields = new List<ColumnFormatEditDto>();
|
||||||
|
|
||||||
|
var fieldOrder = 0;
|
||||||
|
var editGroupOrder = 0;
|
||||||
|
foreach (var group in Groups ?? [])
|
||||||
|
{
|
||||||
|
editGroupOrder++;
|
||||||
|
var editOrder = 0;
|
||||||
|
foreach (var item in group.Items ?? [])
|
||||||
|
{
|
||||||
|
fieldOrder++;
|
||||||
|
var inEditingForm = item.IncludeInEditingForm && item.FieldName != KeyFieldName;
|
||||||
|
if (inEditingForm)
|
||||||
|
editOrder++;
|
||||||
|
|
||||||
|
var field = new ColumnFormatEditDto
|
||||||
|
{
|
||||||
|
ListFormCode = listFormCode,
|
||||||
|
FieldName = item.FieldName,
|
||||||
|
CaptionName = item.CaptionName,
|
||||||
|
Visible = item.FieldName != KeyFieldName,
|
||||||
|
IsActive = true,
|
||||||
|
AllowSearch = true,
|
||||||
|
Width = 0, // Fit columns icin tum alanlar sifir genislikle baslar.
|
||||||
|
ListOrderNo = fieldOrder,
|
||||||
|
EditGroupOrderNo = item.IncludeInEditingForm ? editGroupOrder : null,
|
||||||
|
EditOrderNo = inEditingForm ? editOrder : null,
|
||||||
|
EditorType2 = item.EditorType,
|
||||||
|
ColSpan = item.ColSpan,
|
||||||
|
EditorOptions = string.IsNullOrWhiteSpace(item.EditorOptions) ? null : item.EditorOptions,
|
||||||
|
EditorScript = string.IsNullOrWhiteSpace(item.EditorScript) ? null : item.EditorScript,
|
||||||
|
SourceDbType = item.DbSourceType,
|
||||||
|
CultureName = PlatformConsts.DefaultLanguage,
|
||||||
|
};
|
||||||
|
|
||||||
|
field.PermissionJson = WizardConsts.DefaultFieldPermissionJson(code);
|
||||||
|
field.ColumnCustomizationJson = WizardConsts.DefaultColumnCustomizationJson;
|
||||||
|
field.ColumnFilterJson = WizardConsts.DefaultColumnFilteringJson;
|
||||||
|
field.PivotSettingsJson = WizardConsts.DefaultPivotSettingsJson;
|
||||||
|
field.LookupJson = !string.IsNullOrWhiteSpace(item.LookupQuery)
|
||||||
|
? WizardConsts.DefaultLookupJson(item.LookupDataSourceType, item.DisplayExpr, item.ValueExpr, item.LookupQuery)
|
||||||
|
: null;
|
||||||
|
field.ValidationRuleJson = inEditingForm && item.IsRequired
|
||||||
|
? WizardConsts.DefaultValidationRuleRequiredJson
|
||||||
|
: null;
|
||||||
|
|
||||||
|
fields.Add(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tasarimci uzerinde degisen ekrani wizard bolumune geri yazar. Wizard adimlarinin sordugu
|
||||||
|
/// her sorunun ListForm sozlesmesinde bir karsiligi vardir; senkron kalmazsa wizard yeniden
|
||||||
|
/// deploy edildiginde tasarimcida yapilan degisiklikler geri alinirdi.
|
||||||
|
/// </summary>
|
||||||
|
public void RefreshFrom(GridOptionsEditDto listForm, IReadOnlyList<ColumnFormatEditDto> fields)
|
||||||
|
{
|
||||||
|
ListFormCode = listForm.ListFormCode;
|
||||||
|
DataSourceCode = listForm.DataSourceCode;
|
||||||
|
SelectCommandType = listForm.SelectCommandType;
|
||||||
|
SelectCommand = listForm.SelectCommand;
|
||||||
|
KeyFieldName = listForm.KeyFieldName;
|
||||||
|
KeyFieldDbSourceType = listForm.KeyFieldDbSourceType;
|
||||||
|
IsTenant = listForm.IsTenant;
|
||||||
|
IsBranch = listForm.IsBranch;
|
||||||
|
IsOrganizationUnit = listForm.IsOrganizationUnit;
|
||||||
|
|
||||||
|
var layout = listForm.LayoutDto;
|
||||||
|
DefaultLayout = layout.DefaultLayout;
|
||||||
|
Grid = layout.Grid;
|
||||||
|
Card = layout.Card;
|
||||||
|
Pivot = layout.Pivot;
|
||||||
|
Tree = layout.Tree;
|
||||||
|
Chart = layout.Chart;
|
||||||
|
Gantt = layout.Gantt;
|
||||||
|
Scheduler = layout.Scheduler;
|
||||||
|
Todo = layout.Todo;
|
||||||
|
|
||||||
|
var editing = listForm.EditingOptionDto;
|
||||||
|
AllowAdding = editing.AllowAdding;
|
||||||
|
AllowUpdating = editing.AllowUpdating;
|
||||||
|
AllowDeleting = editing.AllowDeleting;
|
||||||
|
AllowDetail = editing.AllowDetail;
|
||||||
|
ConfirmDelete = editing.ConfirmDelete;
|
||||||
|
|
||||||
|
TreeOptionDto = listForm.TreeOptionDto;
|
||||||
|
GanttOptionDto = listForm.GanttOptionDto;
|
||||||
|
SchedulerOptionDto = listForm.SchedulerOptionDto;
|
||||||
|
TodoOptionDto = listForm.TodoOptionDto;
|
||||||
|
WorkflowDto = listForm.WorkflowDto;
|
||||||
|
SubForms = [.. listForm.SubFormsDto];
|
||||||
|
Widgets = [.. listForm.WidgetsDto];
|
||||||
|
Groups = BuildGroups(listForm, fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Alanlari wizard'in grup/oge modeline geri cevirir. Grup basliklari ve kolon sayilari
|
||||||
|
/// duzenleme formu tanimindan, alan sirasi ise listedeki sirasindan gelir. Alan basliklarinin
|
||||||
|
/// iki dilli metinleri ListForm alan kaydinda tutulmadigi icin mevcut wizard cevaplarindan
|
||||||
|
/// tasinir; aksi halde ekran tasarimcidan kaydedildiginde dil metinleri kaybolurdu.
|
||||||
|
/// </summary>
|
||||||
|
private List<WizardColumnGroupInputDto> BuildGroups(GridOptionsEditDto listForm, IReadOnlyList<ColumnFormatEditDto> fields)
|
||||||
|
{
|
||||||
|
var editingForm = listForm.EditingFormDto ?? [];
|
||||||
|
var captions = (Groups ?? [])
|
||||||
|
.SelectMany(g => g.Items ?? [])
|
||||||
|
.Where(i => !string.IsNullOrWhiteSpace(i.FieldName))
|
||||||
|
.GroupBy(i => i.FieldName, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
|
||||||
|
var groups = new List<WizardColumnGroupInputDto>();
|
||||||
|
var groupIndexByOrder = new Dictionary<int, int>();
|
||||||
|
|
||||||
|
foreach (var field in fields.OrderBy(f => f.ListOrderNo))
|
||||||
|
{
|
||||||
|
// Duzenleme formunda yer almayan alanlar da wizard'da gorunmelidir; grup bilgisi
|
||||||
|
// olmadigi icin ilk gruba, duzenleme disi olarak eklenirler.
|
||||||
|
var groupOrder = field.EditGroupOrderNo ?? 1;
|
||||||
|
if (!groupIndexByOrder.TryGetValue(groupOrder, out var index))
|
||||||
|
{
|
||||||
|
var definition = editingForm.FirstOrDefault(x => x.Order == groupOrder);
|
||||||
|
groups.Add(new WizardColumnGroupInputDto
|
||||||
|
{
|
||||||
|
Caption = definition?.Caption ?? string.Empty,
|
||||||
|
ColCount = definition?.ColCount ?? 2,
|
||||||
|
});
|
||||||
|
index = groups.Count - 1;
|
||||||
|
groupIndexByOrder[groupOrder] = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lookup = field.LookupDto;
|
||||||
|
captions.TryGetValue(field.FieldName ?? string.Empty, out var previous);
|
||||||
|
|
||||||
|
groups[index].Items.Add(new WizardColumnItemInputDto
|
||||||
|
{
|
||||||
|
FieldName = field.FieldName,
|
||||||
|
CaptionName = field.CaptionName,
|
||||||
|
EditorType = field.EditorType2,
|
||||||
|
EditorOptions = field.EditorOptions,
|
||||||
|
EditorScript = field.EditorScript,
|
||||||
|
ColSpan = field.ColSpan ?? 1,
|
||||||
|
IsRequired = field.ValidationRuleDto.Exists(r =>
|
||||||
|
string.Equals(r.Type, nameof(UiColumnValidationRuleTypeEnum.required), StringComparison.OrdinalIgnoreCase)),
|
||||||
|
IncludeInEditingForm = field.EditGroupOrderNo.HasValue,
|
||||||
|
DbSourceType = field.SourceDbType,
|
||||||
|
LookupDataSourceType = lookup.DataSourceType,
|
||||||
|
ValueExpr = lookup.ValueExpr,
|
||||||
|
DisplayExpr = lookup.DisplayExpr,
|
||||||
|
LookupQuery = lookup.LookupQuery,
|
||||||
|
EnglishCaption = previous?.EnglishCaption,
|
||||||
|
TurkishCaption = previous?.TurkishCaption,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Duzenleme formunun genisligi ve yuksekligi gruplarin kolon sayisindan ve toplam satir
|
||||||
|
/// sayisindan hesaplanir; boylece popup icerigi kirpilmadan acilir.
|
||||||
|
/// </summary>
|
||||||
|
private (int Width, int Height) CalculateEditFormSize()
|
||||||
|
{
|
||||||
|
var groupLayouts = (Groups ?? [])
|
||||||
|
.Select(g =>
|
||||||
|
{
|
||||||
|
var itemCount = (g.Items ?? []).Count(i => i.IncludeInEditingForm && i.FieldName != KeyFieldName);
|
||||||
|
var columnCount = Math.Clamp(g.ColCount, 1, WizardConsts.EditFormMaxColumnCount);
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
ColumnCount = columnCount,
|
||||||
|
RowCount = (int)Math.Ceiling(itemCount / (double)columnCount)
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var formColumnCount = groupLayouts.Count > 0 ? groupLayouts.Max(x => x.ColumnCount) : 1;
|
||||||
|
var totalRowCount = groupLayouts.Sum(x => x.RowCount);
|
||||||
|
|
||||||
|
return (
|
||||||
|
WizardConsts.EditFormMaxWidth / WizardConsts.EditFormMaxColumnCount * formColumnCount,
|
||||||
|
WizardConsts.EditFormHeightDefault + totalRowCount * WizardConsts.EditFormHeightByRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool UsesLayout(string layout, bool enabled)
|
||||||
|
{
|
||||||
|
return enabled || string.Equals(DefaultLayout, layout, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasWorkflow(WorkflowDto workflow)
|
||||||
|
{
|
||||||
|
return workflow != null && (
|
||||||
|
!string.IsNullOrWhiteSpace(workflow.ApprovalUserFieldName) ||
|
||||||
|
!string.IsNullOrWhiteSpace(workflow.ApprovalStatusFieldName) ||
|
||||||
|
workflow.Criteria.Count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Workflow adimlarini kaydedilebilir hale getirir: basliklar tekillestirilir, adim
|
||||||
|
/// kimlikleri ListForm koduyla on eklenir. Seed dosyasina normalize edilmis hali yazilir.
|
||||||
|
/// </summary>
|
||||||
|
private static WorkflowDto NormalizeWorkflow(WorkflowDto workflow, string listFormCode)
|
||||||
|
{
|
||||||
|
workflow ??= new WorkflowDto();
|
||||||
|
workflow.Criteria ??= [];
|
||||||
|
EnsureUniqueWorkflowCriteriaTitles(workflow.Criteria);
|
||||||
|
NormalizeWorkflowCriteriaIds(listFormCode, workflow.Criteria);
|
||||||
|
return workflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureUniqueWorkflowCriteriaTitles(List<ListFormWorkflowCriteriaDto> criteria)
|
||||||
|
{
|
||||||
|
if (criteria.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var baseTitles = criteria
|
||||||
|
.Select(x => string.IsNullOrWhiteSpace(x.Title) ? NormalizeWorkflowTitleFallback(x.Kind) : x.Title.Trim())
|
||||||
|
.ToList();
|
||||||
|
var duplicateTitles = baseTitles
|
||||||
|
.GroupBy(x => x, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Where(x => x.Count() > 1)
|
||||||
|
.Select(x => x.Key)
|
||||||
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||||
|
var titleCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
var usedTitles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
foreach (var item in criteria)
|
||||||
|
{
|
||||||
|
var baseTitle = string.IsNullOrWhiteSpace(item.Title) ? NormalizeWorkflowTitleFallback(item.Kind) : item.Title.Trim();
|
||||||
|
var title = baseTitle;
|
||||||
|
|
||||||
|
if (duplicateTitles.Contains(baseTitle))
|
||||||
|
{
|
||||||
|
titleCounts.TryGetValue(baseTitle, out var count);
|
||||||
|
count++;
|
||||||
|
titleCounts[baseTitle] = count;
|
||||||
|
title = $"{baseTitle}{count}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usedTitles.Contains(title))
|
||||||
|
{
|
||||||
|
var index = 1;
|
||||||
|
var candidate = $"{title}{index}";
|
||||||
|
while (usedTitles.Contains(candidate))
|
||||||
|
{
|
||||||
|
index++;
|
||||||
|
candidate = $"{title}{index}";
|
||||||
|
}
|
||||||
|
|
||||||
|
title = candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.Title = title;
|
||||||
|
usedTitles.Add(title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeWorkflowTitleFallback(string kind)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(kind) ? "Step" : kind.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void NormalizeWorkflowCriteriaIds(string listFormCode, List<ListFormWorkflowCriteriaDto> criteria)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(listFormCode) || criteria.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var item in criteria)
|
||||||
|
{
|
||||||
|
item.Id = NormalizeWorkflowCriteriaId(listFormCode, item.Id);
|
||||||
|
item.ListFormCode = listFormCode;
|
||||||
|
item.NextOnStart = NormalizeWorkflowTargetId(listFormCode, item.NextOnStart);
|
||||||
|
item.NextOnTrue = NormalizeWorkflowTargetId(listFormCode, item.NextOnTrue);
|
||||||
|
item.NextOnFalse = NormalizeWorkflowTargetId(listFormCode, item.NextOnFalse);
|
||||||
|
item.NextOnApprove = NormalizeWorkflowTargetId(listFormCode, item.NextOnApprove);
|
||||||
|
item.NextOnReject = NormalizeWorkflowTargetId(listFormCode, item.NextOnReject);
|
||||||
|
|
||||||
|
foreach (var outcome in item.CompareOutcomes ?? [])
|
||||||
|
{
|
||||||
|
outcome.TargetId = NormalizeWorkflowTargetId(listFormCode, outcome.TargetId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeWorkflowTargetId(string listFormCode, string id)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(id) ? string.Empty : NormalizeWorkflowCriteriaId(listFormCode, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeWorkflowCriteriaId(string listFormCode, string id)
|
||||||
|
{
|
||||||
|
var trimmed = id?.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(trimmed))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
if (trimmed.StartsWith($"{listFormCode}-", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return trimmed;
|
||||||
|
|
||||||
|
return trimmed.StartsWith("N", StringComparison.OrdinalIgnoreCase)
|
||||||
|
? $"{listFormCode}-{trimmed}"
|
||||||
|
: trimmed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
using Sozsoft.Platform.Enums;
|
using Sozsoft.Platform.Enums;
|
||||||
|
using Volo.Abp.Application.Dtos;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.ListForms;
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
||||||
|
|
@ -12,6 +17,15 @@ public class WizardSeedFileDto
|
||||||
{
|
{
|
||||||
public ListFormWizardDto Wizard { get; set; }
|
public ListFormWizardDto Wizard { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wizard'in urettigi ekranin ListForm sozlesmesi. Tasarimci (ListForm detayi) uzerinde
|
||||||
|
/// yapilan her degisiklik buraya geri yazilir; seeder ekrani bu bolumden kurar.
|
||||||
|
/// </summary>
|
||||||
|
public GridOptionsEditDto ListForm { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Ekranin alanlari; ListForm detayindaki alan tanimlariyla ayni sozlesme.</summary>
|
||||||
|
public List<ColumnFormatEditDto> Fields { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>Tabloda IsDeleted alanı olup olmadığını belirtir (soft delete desteği).</summary>
|
/// <summary>Tabloda IsDeleted alanı olup olmadığını belirtir (soft delete desteği).</summary>
|
||||||
public bool IsDeletedField { get; set; }
|
public bool IsDeletedField { get; set; }
|
||||||
|
|
||||||
|
|
@ -23,6 +37,274 @@ public class WizardSeedFileDto
|
||||||
/// Daha önce var olan kayıtlar buraya eklenmez, silme işleminde sadece bu liste kullanılır.
|
/// Daha önce var olan kayıtlar buraya eklenmez, silme işleminde sadece bu liste kullanılır.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public WizardInsertedRecordsDto InsertedRecords { get; set; } = new();
|
public WizardInsertedRecordsDto InsertedRecords { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dosyaya yazmadan once ortama ozgu alanlari temizler. Kayit kimlikleri ve audit damgalari
|
||||||
|
/// veritabanina aittir; dosyada tasinirlarsa her ortamda anlamsiz fark uretirler. EditType ise
|
||||||
|
/// yalnizca tasarimcinin hangi sekmeyi kaydettigini bildiren istek alanidir.
|
||||||
|
/// </summary>
|
||||||
|
private void ClearRuntimeFields()
|
||||||
|
{
|
||||||
|
if (Wizard != null)
|
||||||
|
Wizard.EditFileName = null;
|
||||||
|
|
||||||
|
if (ListForm != null)
|
||||||
|
ListForm.EditType = null;
|
||||||
|
|
||||||
|
ClearAudit(ListForm);
|
||||||
|
|
||||||
|
foreach (var field in Fields ?? [])
|
||||||
|
{
|
||||||
|
field.EditType = null;
|
||||||
|
ClearAudit(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ekranin sozlesmesi <see cref="ListForm"/> ve <see cref="Fields"/> bolumlerindedir; wizard
|
||||||
|
/// bolumunde ayni degerlerin ikinci bir kopyasi tutulmaz. Bu anahtarlar dosyaya yazilmadan
|
||||||
|
/// once wizard bolumunden cikarilir, okurken <see cref="Rehydrate"/> ile geri doldurulur.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly string[] ListFormOwnedWizardKeys =
|
||||||
|
[
|
||||||
|
nameof(ListFormWizardDto.IsTenant), nameof(ListFormWizardDto.IsBranch), nameof(ListFormWizardDto.IsOrganizationUnit),
|
||||||
|
nameof(ListFormWizardDto.AllowAdding), nameof(ListFormWizardDto.AllowUpdating), nameof(ListFormWizardDto.AllowDeleting),
|
||||||
|
nameof(ListFormWizardDto.AllowDetail), nameof(ListFormWizardDto.ConfirmDelete),
|
||||||
|
nameof(ListFormWizardDto.DefaultLayout), nameof(ListFormWizardDto.Grid), nameof(ListFormWizardDto.Card),
|
||||||
|
nameof(ListFormWizardDto.Pivot), nameof(ListFormWizardDto.Tree), nameof(ListFormWizardDto.Chart),
|
||||||
|
nameof(ListFormWizardDto.Gantt), nameof(ListFormWizardDto.Scheduler), nameof(ListFormWizardDto.Todo),
|
||||||
|
nameof(ListFormWizardDto.DataSourceCode), nameof(ListFormWizardDto.SelectCommandType), nameof(ListFormWizardDto.SelectCommand),
|
||||||
|
nameof(ListFormWizardDto.KeyFieldName), nameof(ListFormWizardDto.KeyFieldDbSourceType),
|
||||||
|
nameof(ListFormWizardDto.TreeOptionDto), nameof(ListFormWizardDto.GanttOptionDto),
|
||||||
|
nameof(ListFormWizardDto.SchedulerOptionDto), nameof(ListFormWizardDto.TodoOptionDto),
|
||||||
|
nameof(ListFormWizardDto.WorkflowDto), nameof(ListFormWizardDto.SubForms), nameof(ListFormWizardDto.Widgets),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// <summary>Grup ve alan tanimlarinin <see cref="Fields"/> bolumunde karsiligi olan anahtarlari.</summary>
|
||||||
|
private static readonly string[] FieldsOwnedGroupKeys =
|
||||||
|
[
|
||||||
|
nameof(WizardColumnGroupInputDto.Caption), nameof(WizardColumnGroupInputDto.ColCount),
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] FieldsOwnedItemKeys =
|
||||||
|
[
|
||||||
|
nameof(WizardColumnItemInputDto.EditorType), nameof(WizardColumnItemInputDto.EditorOptions),
|
||||||
|
nameof(WizardColumnItemInputDto.EditorScript), nameof(WizardColumnItemInputDto.ColSpan),
|
||||||
|
nameof(WizardColumnItemInputDto.IsRequired), nameof(WizardColumnItemInputDto.IncludeInEditingForm),
|
||||||
|
nameof(WizardColumnItemInputDto.DbSourceType), nameof(WizardColumnItemInputDto.LookupDataSourceType),
|
||||||
|
nameof(WizardColumnItemInputDto.ValueExpr), nameof(WizardColumnItemInputDto.DisplayExpr),
|
||||||
|
nameof(WizardColumnItemInputDto.LookupQuery),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// <summary>Dosyada `Menu` basligi altinda toplanan wizard alanlari.</summary>
|
||||||
|
private static readonly string[] MenuKeys =
|
||||||
|
[
|
||||||
|
nameof(ListFormWizardDto.CreateMenu), nameof(ListFormWizardDto.MenuCode), nameof(ListFormWizardDto.MenuOrder),
|
||||||
|
nameof(ListFormWizardDto.MenuUrl), nameof(ListFormWizardDto.MenuIcon), nameof(ListFormWizardDto.MenuParentCode),
|
||||||
|
nameof(ListFormWizardDto.MenuParentIcon),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dosyada daha okunakli adla yazilan alanlar. Modul, wizard'in ana bilgisidir; menu ve
|
||||||
|
/// yetkilerin hangi modul altinda filtrelenecegini belirler.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly (string Property, string SeedKey)[] RenamedKeys =
|
||||||
|
[
|
||||||
|
(nameof(ListFormWizardDto.MenuParentModuleId), "ModuleId"),
|
||||||
|
(nameof(ListFormWizardDto.ListFormCode), WizardCodeKey),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ekranin kodu wizard'in kimligidir: menu kodu, yetki adlari ve dil anahtarlari bu koddan
|
||||||
|
/// turer. Dosyada bu nedenle ekran adiyla degil wizard adiyla anilir.
|
||||||
|
/// </summary>
|
||||||
|
private const string WizardCodeKey = "WizardCode";
|
||||||
|
|
||||||
|
/// <summary>Dosyadaki okuma sirasi: kimlik, kapsam, sonra menu/dil/alan bloklari.</summary>
|
||||||
|
private static readonly string[] WizardKeyOrder =
|
||||||
|
[
|
||||||
|
nameof(ListFormWizardDto.ComponentKind), nameof(ListFormWizardDto.WizardName), WizardCodeKey,
|
||||||
|
nameof(ListFormWizardDto.SeededAt), nameof(ListFormWizardDto.CustomComponentName),
|
||||||
|
nameof(ListFormWizardDto.PermissionGroupName), nameof(ListFormWizardDto.PermissionGroupDisplayNameEn),
|
||||||
|
nameof(ListFormWizardDto.PermissionGroupDisplayNameTr), "ModuleId",
|
||||||
|
nameof(ListFormWizardDto.DataSourceConnectionString),
|
||||||
|
MenuSection, LanguageSection, nameof(ListFormWizardDto.Groups),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dosyada `Language` basligi altinda toplanan dil metinleri. ListForm yalnizca dil
|
||||||
|
/// anahtarini tasir (`Title = "{ListFormCode}.Title"`); metinlerin tek kaynagi burasidir.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly string[] LanguageKeys =
|
||||||
|
[
|
||||||
|
nameof(ListFormWizardDto.LanguageTextMenuEn), nameof(ListFormWizardDto.LanguageTextMenuTr),
|
||||||
|
nameof(ListFormWizardDto.LanguageTextTitleEn), nameof(ListFormWizardDto.LanguageTextTitleTr),
|
||||||
|
nameof(ListFormWizardDto.LanguageTextDescEn), nameof(ListFormWizardDto.LanguageTextDescTr),
|
||||||
|
nameof(ListFormWizardDto.LanguageTextMenuParentEn), nameof(ListFormWizardDto.LanguageTextMenuParentTr),
|
||||||
|
];
|
||||||
|
|
||||||
|
private const string MenuSection = "Menu";
|
||||||
|
private const string LanguageSection = "Language";
|
||||||
|
|
||||||
|
private static readonly JsonSerializerOptions SeedWriteOptions = new() { WriteIndented = true };
|
||||||
|
private static readonly JsonSerializerOptions SeedReadOptions = new() { PropertyNameCaseInsensitive = true };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dosyaya yazilacak JSON'u uretir: once ortama ozgu alanlar temizlenir, sonra wizard
|
||||||
|
/// bolumunden ekranin sozlesmesinde zaten bulunan alanlar cikarilir. Wizard bolumunde
|
||||||
|
/// yalnizca ekrandan uretilemeyen bilgiler kalir: menu, yetki, dil metinleri ve alan
|
||||||
|
/// basliklarinin iki dilli karsiliklari.
|
||||||
|
/// </summary>
|
||||||
|
public string ToSeedJson()
|
||||||
|
{
|
||||||
|
ClearRuntimeFields();
|
||||||
|
|
||||||
|
var node = JsonSerializer.SerializeToNode(this, SeedWriteOptions);
|
||||||
|
|
||||||
|
// Custom yolunda ListForm bolumu yoktur; cikarilan alanlarin geri doldurulacagi bir
|
||||||
|
// kaynak olmadigi icin wizard bolumu oldugu gibi birakilir.
|
||||||
|
if (ListForm != null && node?[nameof(Wizard)] is JsonObject wizard)
|
||||||
|
{
|
||||||
|
foreach (var key in ListFormOwnedWizardKeys)
|
||||||
|
wizard.Remove(key);
|
||||||
|
|
||||||
|
foreach (var group in wizard[nameof(ListFormWizardDto.Groups)] as JsonArray ?? [])
|
||||||
|
{
|
||||||
|
if (group is not JsonObject groupObject)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (var key in FieldsOwnedGroupKeys)
|
||||||
|
groupObject.Remove(key);
|
||||||
|
|
||||||
|
foreach (var item in groupObject[nameof(WizardColumnGroupInputDto.Items)] as JsonArray ?? [])
|
||||||
|
{
|
||||||
|
if (item is JsonObject itemObject)
|
||||||
|
{
|
||||||
|
foreach (var key in FieldsOwnedItemKeys)
|
||||||
|
itemObject.Remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node?[nameof(Wizard)] is JsonObject wizardNode)
|
||||||
|
{
|
||||||
|
// Baglanti cumlesi yalnizca yeni bir veri kaynagi tanimlanirken doludur; bos degeri
|
||||||
|
// dosyada tasimanin anlami yok.
|
||||||
|
if (string.IsNullOrWhiteSpace(Wizard?.DataSourceConnectionString))
|
||||||
|
wizardNode.Remove(nameof(ListFormWizardDto.DataSourceConnectionString));
|
||||||
|
|
||||||
|
// Menu uretmek varsayilan davranistir; dosyada yalnizca istisna (menusuz wizard)
|
||||||
|
// isaretlenir. Menu alanlari menusuz wizardda da kullanildigi icin (yetki kokunu
|
||||||
|
// veren MenuCode, Custom yolunda rotayi veren MenuUrl) blok kaldirilmaz.
|
||||||
|
if (Wizard?.CreateMenu != false)
|
||||||
|
wizardNode.Remove(nameof(ListFormWizardDto.CreateMenu));
|
||||||
|
|
||||||
|
foreach (var (property, seedKey) in RenamedKeys)
|
||||||
|
Rename(wizardNode, property, seedKey);
|
||||||
|
|
||||||
|
MoveIntoSection(wizardNode, MenuSection, MenuKeys);
|
||||||
|
MoveIntoSection(wizardNode, LanguageSection, LanguageKeys);
|
||||||
|
|
||||||
|
node[nameof(Wizard)] = Reorder(wizardNode, WizardKeyOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node?.ToJsonString(SeedWriteOptions) ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Seed dosyasini okur: once `Menu`/`Language` basliklari duzlestirilir (sozlesme duz
|
||||||
|
/// oldugu icin), sonra wizard bolumu ekranin sozlesmesinden tamamlanir.
|
||||||
|
/// </summary>
|
||||||
|
public static WizardSeedFileDto FromSeedJson(string json)
|
||||||
|
{
|
||||||
|
var node = JsonNode.Parse(json);
|
||||||
|
if (node?[nameof(Wizard)] is JsonObject wizardNode)
|
||||||
|
{
|
||||||
|
FlattenSection(wizardNode, MenuSection);
|
||||||
|
FlattenSection(wizardNode, LanguageSection);
|
||||||
|
|
||||||
|
foreach (var (property, seedKey) in RenamedKeys)
|
||||||
|
Rename(wizardNode, seedKey, property);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node.Deserialize<WizardSeedFileDto>(SeedReadOptions)?.Rehydrate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Rename(JsonObject wizard, string from, string to)
|
||||||
|
{
|
||||||
|
if (wizard.Remove(from, out var value))
|
||||||
|
wizard[to] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wizard bolumunu verilen anahtar sirasina gore yeniden kurar; listede olmayan anahtarlar
|
||||||
|
/// mevcut siralarini koruyarak sona eklenir. JSON nesnesi ekleme sirasini korudugu icin
|
||||||
|
/// siralamanin tek yolu nesneyi yeniden olusturmaktir.
|
||||||
|
/// </summary>
|
||||||
|
private static JsonObject Reorder(JsonObject wizard, string[] keyOrder)
|
||||||
|
{
|
||||||
|
var ordered = new JsonObject();
|
||||||
|
foreach (var key in keyOrder.Concat(wizard.Select(x => x.Key).ToArray()))
|
||||||
|
{
|
||||||
|
if (!ordered.ContainsKey(key) && wizard.Remove(key, out var value))
|
||||||
|
ordered[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ordered;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verilen anahtarlari wizard bolumunden alip tek bir baslik altina tasir.</summary>
|
||||||
|
private static void MoveIntoSection(JsonObject wizard, string section, string[] keys)
|
||||||
|
{
|
||||||
|
var target = new JsonObject();
|
||||||
|
foreach (var key in keys)
|
||||||
|
{
|
||||||
|
if (wizard.Remove(key, out var value))
|
||||||
|
target[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.Count > 0)
|
||||||
|
wizard[section] = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Baslik altindaki alanlari wizard bolumune geri tasir.</summary>
|
||||||
|
private static void FlattenSection(JsonObject wizard, string section)
|
||||||
|
{
|
||||||
|
if (!wizard.Remove(section, out var value) || value is not JsonObject group)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var key in group.Select(x => x.Key).ToArray())
|
||||||
|
{
|
||||||
|
if (group.Remove(key, out var item))
|
||||||
|
wizard[key] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dosyadan okunan wizard bolumunu ekranin sozlesmesinden tamamlar. Dosyada tekrar edilmeyen
|
||||||
|
/// alanlar (veri kaynagi, duzen, duzenleme izinleri, alan tanimlari) <see cref="ListForm"/> ve
|
||||||
|
/// <see cref="Fields"/> uzerinden doldurulur; wizard'i okuyan her kod tam nesneyi gorur.
|
||||||
|
/// </summary>
|
||||||
|
private WizardSeedFileDto Rehydrate()
|
||||||
|
{
|
||||||
|
if (Wizard != null && ListForm != null)
|
||||||
|
Wizard.RefreshFrom(ListForm, Fields ?? []);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ClearAudit(AuditedEntityDto<Guid> dto)
|
||||||
|
{
|
||||||
|
if (dto == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dto.Id = default;
|
||||||
|
dto.CreationTime = default;
|
||||||
|
dto.CreatorId = null;
|
||||||
|
dto.LastModificationTime = null;
|
||||||
|
dto.LastModifierId = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,16 @@ public class ListFormFieldsAppService : CrudAppService<
|
||||||
{
|
{
|
||||||
public IRepository<ListForm, Guid> RepoListForm { get; }
|
public IRepository<ListForm, Guid> RepoListForm { get; }
|
||||||
|
|
||||||
|
private readonly IWizardSeedSynchronizer wizardSeedSynchronizer;
|
||||||
|
|
||||||
public ListFormFieldsAppService(
|
public ListFormFieldsAppService(
|
||||||
IRepository<ListFormField, Guid> _repository,
|
IRepository<ListFormField, Guid> _repository,
|
||||||
IRepository<ListForm, Guid> _repoListForm
|
IRepository<ListForm, Guid> _repoListForm,
|
||||||
|
IWizardSeedSynchronizer _wizardSeedSynchronizer
|
||||||
) : base(_repository)
|
) : base(_repository)
|
||||||
{
|
{
|
||||||
RepoListForm = _repoListForm;
|
RepoListForm = _repoListForm;
|
||||||
|
wizardSeedSynchronizer = _wizardSeedSynchronizer;
|
||||||
|
|
||||||
CreatePolicyName = $"{AppCodes.Listforms.ListformField}.Create";
|
CreatePolicyName = $"{AppCodes.Listforms.ListformField}.Create";
|
||||||
UpdatePolicyName = $"{AppCodes.Listforms.ListformField}.Update";
|
UpdatePolicyName = $"{AppCodes.Listforms.ListformField}.Update";
|
||||||
|
|
@ -119,7 +123,12 @@ public class ListFormFieldsAppService : CrudAppService<
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return await base.CreateAsync(input);
|
var created = await base.CreateAsync(input);
|
||||||
|
|
||||||
|
// Ekran wizard ile uretildiyse seed dosyasi da guncel alan listesini tasimalidir.
|
||||||
|
await wizardSeedSynchronizer.SyncAsync(input.ListFormCode);
|
||||||
|
|
||||||
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<ColumnFormatEditDto> UpdateAsync(Guid id, ColumnFormatEditDto input)
|
public override async Task<ColumnFormatEditDto> UpdateAsync(Guid id, ColumnFormatEditDto input)
|
||||||
|
|
@ -214,9 +223,20 @@ public class ListFormFieldsAppService : CrudAppService<
|
||||||
|
|
||||||
await Repository.UpdateAsync(item, autoSave: true);
|
await Repository.UpdateAsync(item, autoSave: true);
|
||||||
|
|
||||||
|
await wizardSeedSynchronizer.SyncAsync(item.ListFormCode);
|
||||||
|
|
||||||
return await MapToGetOutputDtoAsync(item);
|
return await MapToGetOutputDtoAsync(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task DeleteAsync(Guid id)
|
||||||
|
{
|
||||||
|
var listFormCode = (await GetEntityByIdAsync(id)).ListFormCode;
|
||||||
|
|
||||||
|
await base.DeleteAsync(id);
|
||||||
|
|
||||||
|
await wizardSeedSynchronizer.SyncAsync(listFormCode);
|
||||||
|
}
|
||||||
|
|
||||||
[Authorize(AppCodes.Listforms.ListformField + ".Create")]
|
[Authorize(AppCodes.Listforms.ListformField + ".Create")]
|
||||||
public async Task<CopyListFormFieldColumnDto> CopyField(CopyListFormFieldColumnDto input)
|
public async Task<CopyListFormFieldColumnDto> CopyField(CopyListFormFieldColumnDto input)
|
||||||
{
|
{
|
||||||
|
|
@ -324,6 +344,8 @@ public class ListFormFieldsAppService : CrudAppService<
|
||||||
|
|
||||||
await Repository.InsertAsync(field);
|
await Repository.InsertAsync(field);
|
||||||
|
|
||||||
|
await wizardSeedSynchronizer.SyncAsync(field.ListFormCode);
|
||||||
|
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,15 @@ namespace Sozsoft.Platform.ListForms.Administration;
|
||||||
public class ListFormJsonRowAppService : PlatformAppService
|
public class ListFormJsonRowAppService : PlatformAppService
|
||||||
{
|
{
|
||||||
private readonly IRepository<ListForm, Guid> repository;
|
private readonly IRepository<ListForm, Guid> repository;
|
||||||
|
private readonly IWizardSeedSynchronizer wizardSeedSynchronizer;
|
||||||
|
|
||||||
public ListFormJsonRowAppService(
|
public ListFormJsonRowAppService(
|
||||||
IRepository<ListForm, Guid> _repository
|
IRepository<ListForm, Guid> _repository,
|
||||||
|
IWizardSeedSynchronizer _wizardSeedSynchronizer
|
||||||
) : base()
|
) : base()
|
||||||
{
|
{
|
||||||
repository = _repository;
|
repository = _repository;
|
||||||
|
wizardSeedSynchronizer = _wizardSeedSynchronizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<dynamic> GetAllAsync(Guid id, string field)
|
public async Task<dynamic> GetAllAsync(Guid id, string field)
|
||||||
|
|
@ -183,6 +186,9 @@ public class ListFormJsonRowAppService : PlatformAppService
|
||||||
}
|
}
|
||||||
|
|
||||||
await repository.UpdateAsync(listForm);
|
await repository.UpdateAsync(listForm);
|
||||||
|
|
||||||
|
// Ekran wizard ile uretildiyse seed dosyasi da guncel halini tasimalidir.
|
||||||
|
await wizardSeedSynchronizer.SyncAsync(listForm.ListFormCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateAsync(Guid id, CrudFieldsDefaultValueJsonItemDto model)
|
public async Task UpdateAsync(Guid id, CrudFieldsDefaultValueJsonItemDto model)
|
||||||
|
|
@ -237,6 +243,9 @@ public class ListFormJsonRowAppService : PlatformAppService
|
||||||
}
|
}
|
||||||
|
|
||||||
await repository.UpdateAsync(listForm);
|
await repository.UpdateAsync(listForm);
|
||||||
|
|
||||||
|
// Ekran wizard ile uretildiyse seed dosyasi da guncel halini tasimalidir.
|
||||||
|
await wizardSeedSynchronizer.SyncAsync(listForm.ListFormCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteAsync(Guid id, string field, int index)
|
public async Task DeleteAsync(Guid id, string field, int index)
|
||||||
|
|
@ -291,6 +300,9 @@ public class ListFormJsonRowAppService : PlatformAppService
|
||||||
}
|
}
|
||||||
|
|
||||||
await repository.UpdateAsync(listForm);
|
await repository.UpdateAsync(listForm);
|
||||||
|
|
||||||
|
// Ekran wizard ile uretildiyse seed dosyasi da guncel halini tasimalidir.
|
||||||
|
await wizardSeedSynchronizer.SyncAsync(listForm.ListFormCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ListForm> GetAsync(Guid id)
|
private async Task<ListForm> GetAsync(Guid id)
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,15 @@ public class ListFormsAppService : CrudAppService<
|
||||||
GridOptionsEditDto>, IListFormsAppService
|
GridOptionsEditDto>, IListFormsAppService
|
||||||
{
|
{
|
||||||
private readonly IListFormAuthorizationManager authManager;
|
private readonly IListFormAuthorizationManager authManager;
|
||||||
|
private readonly IWizardSeedSynchronizer wizardSeedSynchronizer;
|
||||||
|
|
||||||
public ListFormsAppService(
|
public ListFormsAppService(
|
||||||
IRepository<ListForm, Guid> _repository,
|
IRepository<ListForm, Guid> _repository,
|
||||||
IListFormAuthorizationManager _authManager) : base(_repository)
|
IListFormAuthorizationManager _authManager,
|
||||||
|
IWizardSeedSynchronizer _wizardSeedSynchronizer) : base(_repository)
|
||||||
{
|
{
|
||||||
authManager = _authManager;
|
authManager = _authManager;
|
||||||
|
wizardSeedSynchronizer = _wizardSeedSynchronizer;
|
||||||
|
|
||||||
CreatePolicyName = $"{AppCodes.Listforms.Listform}.Create";
|
CreatePolicyName = $"{AppCodes.Listforms.Listform}.Create";
|
||||||
UpdatePolicyName = $"{AppCodes.Listforms.Listform}.Update";
|
UpdatePolicyName = $"{AppCodes.Listforms.Listform}.Update";
|
||||||
|
|
@ -239,6 +242,9 @@ public class ListFormsAppService : CrudAppService<
|
||||||
|
|
||||||
await Repository.UpdateAsync(item, autoSave: true);
|
await Repository.UpdateAsync(item, autoSave: true);
|
||||||
|
|
||||||
|
// Ekran wizard ile uretildiyse seed dosyasi da guncel halini tasimalidir.
|
||||||
|
await wizardSeedSynchronizer.SyncAsync(item.ListFormCode);
|
||||||
|
|
||||||
return await MapToGetOutputDtoAsync(item);
|
return await MapToGetOutputDtoAsync(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,6 @@ public class ListFormWizardAppService(
|
||||||
private readonly string cultureNameDefault = PlatformConsts.DefaultLanguage;
|
private readonly string cultureNameDefault = PlatformConsts.DefaultLanguage;
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions SeedJsonReadOptions = new() { PropertyNameCaseInsensitive = true };
|
private static readonly JsonSerializerOptions SeedJsonReadOptions = new() { PropertyNameCaseInsensitive = true };
|
||||||
private static readonly JsonSerializerOptions SeedJsonWriteOptions = new() { WriteIndented = true };
|
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task Create(ListFormWizardDto input)
|
public async Task Create(ListFormWizardDto input)
|
||||||
|
|
@ -92,18 +91,18 @@ public class ListFormWizardAppService(
|
||||||
: AppCodes.Listforms.Wizards.Create);
|
: AppCodes.Listforms.Wizards.Create);
|
||||||
|
|
||||||
if (isEdit)
|
if (isEdit)
|
||||||
|
{
|
||||||
|
// Ilk deploy zamani wizard'in kimligidir; duzenleme onu degistirmez.
|
||||||
|
input.SeededAt = await ReadSeededAtAsync(editFileName!);
|
||||||
await DeleteFileInternalAsync(editFileName!);
|
await DeleteFileInternalAsync(editFileName!);
|
||||||
|
}
|
||||||
|
|
||||||
var wizardName = input.WizardName?.Trim();
|
var wizardName = input.WizardName?.Trim();
|
||||||
if (string.IsNullOrWhiteSpace(wizardName))
|
if (string.IsNullOrWhiteSpace(wizardName))
|
||||||
throw new UserFriendlyException("Wizard adı zorunludur.");
|
throw new UserFriendlyException("Wizard adı zorunludur.");
|
||||||
|
|
||||||
var code = string.IsNullOrWhiteSpace(input.MenuCode)
|
var code = input.ResolveMenuCode();
|
||||||
? WizardConsts.WizardKey(wizardName)
|
var listFormCode = input.ResolveListFormCode();
|
||||||
: input.MenuCode.Trim();
|
|
||||||
var listFormCode = string.IsNullOrWhiteSpace(input.ListFormCode)
|
|
||||||
? code
|
|
||||||
: input.ListFormCode.Trim();
|
|
||||||
// Aşağıdaki tüm kayıtlar normalize edilmiş kodlara göre üretilir.
|
// Aşağıdaki tüm kayıtlar normalize edilmiş kodlara göre üretilir.
|
||||||
input.ListFormCode = listFormCode;
|
input.ListFormCode = listFormCode;
|
||||||
input.MenuCode = code;
|
input.MenuCode = code;
|
||||||
|
|
@ -295,7 +294,7 @@ public class ListFormWizardAppService(
|
||||||
if (isCustomComponent)
|
if (isCustomComponent)
|
||||||
{
|
{
|
||||||
await _languageTextAppService.ClearRedisCacheAsync();
|
await _languageTextAppService.ClearRedisCacheAsync();
|
||||||
await SaveWizardSeedFileAsync(input, false, false, inserted);
|
await SaveWizardSeedFileAsync(input, null, [], false, false, inserted);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -318,21 +317,7 @@ public class ListFormWizardAppService(
|
||||||
inserted.DataSourceCodes.Add(input.DataSourceCode);
|
inserted.DataSourceCodes.Add(input.DataSourceCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build EditingFormJson from wizard groups (sadece editing form alanı olan gruplar)
|
//ListForm ve alanlari - varsa sil, yeniden uret
|
||||||
var editingFormDtos = input.Groups
|
|
||||||
.Select((g, gi) => new { Group = g, Order = gi + 1 })
|
|
||||||
.Where(x => x.Group.Items.Any(i => i.IncludeInEditingForm && i.FieldName != input.KeyFieldName))
|
|
||||||
.Select(x => new EditingFormDto
|
|
||||||
{
|
|
||||||
Order = x.Order,
|
|
||||||
Caption = x.Group.Caption,
|
|
||||||
ColCount = x.Group.ColCount,
|
|
||||||
ColSpan = x.Group.ColCount,
|
|
||||||
ItemType = "group"
|
|
||||||
})
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
//ListForm - varsa sil, yeniden ekle
|
|
||||||
var existingListForm = await repoListForm.FirstOrDefaultAsync(a => a.ListFormCode == input.ListFormCode);
|
var existingListForm = await repoListForm.FirstOrDefaultAsync(a => a.ListFormCode == input.ListFormCode);
|
||||||
if (existingListForm != null)
|
if (existingListForm != null)
|
||||||
{
|
{
|
||||||
|
|
@ -354,115 +339,27 @@ public class ListFormWizardAppService(
|
||||||
var tableColumns = await GetTableColumnNamesAsync(input.DataSourceCode, input.SelectCommandType, input.SelectCommand);
|
var tableColumns = await GetTableColumnNamesAsync(input.DataSourceCode, input.SelectCommandType, input.SelectCommand);
|
||||||
var isDeleted = tableColumns.Contains("IsDeleted");
|
var isDeleted = tableColumns.Contains("IsDeleted");
|
||||||
var isCreated = tableColumns.Contains("CreatorId");
|
var isCreated = tableColumns.Contains("CreatorId");
|
||||||
input.WorkflowDto ??= new WorkflowDto();
|
|
||||||
input.WorkflowDto.Criteria ??= [];
|
// Ekranin sozlesmesi tek fabrikadan uretilir; seed dosyasina yazilan da veritabanina
|
||||||
EnsureUniqueWorkflowCriteriaTitles(input.WorkflowDto.Criteria);
|
// yazilan da ayni nesnedir.
|
||||||
NormalizeWorkflowCriteriaIds(listFormCode, input.WorkflowDto.Criteria);
|
var listFormDto = input.BuildListForm(isDeleted, isCreated);
|
||||||
|
var fieldDtos = input.BuildFields();
|
||||||
var workflow = input.WorkflowDto;
|
var workflow = input.WorkflowDto;
|
||||||
|
|
||||||
await repoListForm.InsertAsync(new ListForm
|
var listForm = new ListForm();
|
||||||
{
|
listFormDto.ApplyTo(listForm);
|
||||||
ListFormType = ListFormTypeEnum.List,
|
await repoListForm.InsertAsync(listForm, autoSave: true);
|
||||||
PageSize = 10,
|
|
||||||
ExportJson = WizardConsts.DefaultExportJson,
|
|
||||||
IsSubForm = false,
|
|
||||||
ShowNote = input.SubForms.Count > 0 || workflow.Criteria.Count > 0,
|
|
||||||
LayoutJson = WizardConsts.DefaultLayoutJson(input.DefaultLayout, input.Grid, input.Card, input.Pivot, input.Tree, input.Chart, input.Gantt, input.Scheduler, input.Todo),
|
|
||||||
CultureName = LanguageCodes.En,
|
|
||||||
ListFormCode = input.ListFormCode,
|
|
||||||
Name = nameLangKey,
|
|
||||||
Title = titleLangKey,
|
|
||||||
Description = descLangKey,
|
|
||||||
DataSourceCode = input.DataSourceCode,
|
|
||||||
IsTenant = input.IsTenant,
|
|
||||||
IsBranch = input.IsBranch,
|
|
||||||
IsOrganizationUnit = input.IsOrganizationUnit,
|
|
||||||
SelectCommandType = input.SelectCommandType,
|
|
||||||
SelectCommand = input.SelectCommand,
|
|
||||||
KeyFieldName = input.KeyFieldName,
|
|
||||||
KeyFieldDbSourceType = input.KeyFieldDbSourceType,
|
|
||||||
DefaultFilter = isDeleted ? WizardConsts.DefaultFilterJson : null,
|
|
||||||
SortMode = PlatformConsts.GridOptions.SortModeSingle,
|
|
||||||
FilterRowJson = WizardConsts.DefaultFilterRowJson(),
|
|
||||||
HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson(),
|
|
||||||
SearchPanelJson = WizardConsts.DefaultSearchPanelJson(),
|
|
||||||
GroupPanelJson = WizardConsts.DefaultGroupPanelJson(),
|
|
||||||
SelectionJson = WizardConsts.DefaultSelectionSingleJson(workflow.Criteria.Count > 0 ? PlatformConsts.GridOptions.SelectionModeSingle : PlatformConsts.GridOptions.SelectionModeNone),
|
|
||||||
ColumnOptionJson = WizardConsts.DefaultColumnOptionJson(),
|
|
||||||
PermissionJson = WizardConsts.DefaultPermissionJson(code),
|
|
||||||
DeleteCommand = isDeleted ? WizardConsts.DefaultDeleteCommand(input.SelectCommand) : null,
|
|
||||||
DeleteFieldsDefaultValueJson = isDeleted ? WizardConsts.DefaultDeleteFieldsDefaultValueJson(input.KeyFieldDbSourceType) : WizardConsts.DefaultDeleteFieldsJsonOnlyId(input.KeyFieldDbSourceType),
|
|
||||||
InsertFieldsDefaultValueJson = isCreated ? WizardConsts.DefaultInsertFieldsDefaultValueJson(input.KeyFieldDbSourceType) : WizardConsts.DefaultInsertFieldsJsonOnlyId(input.KeyFieldDbSourceType),
|
|
||||||
PagerOptionJson = WizardConsts.DefaultPagerOptionJson,
|
|
||||||
EditingOptionJson = WizardConsts.DefaultEditingOptionJson(titleLangKey, 600, 500, input.AllowDeleting, input.AllowAdding, input.AllowUpdating, input.ConfirmDelete, false, input.AllowDetail),
|
|
||||||
EditingFormJson = editingFormDtos.Count > 0 ? JsonSerializer.Serialize(editingFormDtos) : null,
|
|
||||||
SubFormsJson = input.SubForms.Count > 0 ? JsonSerializer.Serialize(input.SubForms) : null,
|
|
||||||
WidgetsJson = input.Widgets.Count > 0 ? JsonSerializer.Serialize(input.Widgets) : null,
|
|
||||||
WorkflowJson = HasWorkflow(workflow) ? JsonSerializer.Serialize(workflow) : null,
|
|
||||||
TreeOptionJson = (input.Tree || input.DefaultLayout == "tree") &&
|
|
||||||
!string.IsNullOrEmpty(input.TreeOptionDto?.ParentIdExpr)
|
|
||||||
? JsonSerializer.Serialize(input.TreeOptionDto)
|
|
||||||
: null,
|
|
||||||
GanttOptionJson = (input.Gantt || input.DefaultLayout == "gantt") &&
|
|
||||||
!string.IsNullOrEmpty(input.GanttOptionDto?.ParentIdExpr)
|
|
||||||
? JsonSerializer.Serialize(input.GanttOptionDto)
|
|
||||||
: null,
|
|
||||||
SchedulerOptionJson = (input.Scheduler || input.DefaultLayout == "scheduler") &&
|
|
||||||
!string.IsNullOrEmpty(input.SchedulerOptionDto?.TextExpr)
|
|
||||||
? JsonSerializer.Serialize(input.SchedulerOptionDto)
|
|
||||||
: null,
|
|
||||||
TodoOptionJson = (input.Todo || input.DefaultLayout == "todo") &&
|
|
||||||
!string.IsNullOrEmpty(input.TodoOptionDto?.TitleExpr) &&
|
|
||||||
!string.IsNullOrEmpty(input.TodoOptionDto?.StatusExpr)
|
|
||||||
? JsonSerializer.Serialize(input.TodoOptionDto)
|
|
||||||
: null,
|
|
||||||
}, autoSave: true);
|
|
||||||
|
|
||||||
// ListFormField - each item in each group becomes a visible field record
|
foreach (var fieldDto in fieldDtos)
|
||||||
var fieldOrder = 0;
|
|
||||||
var editGroupOrder = 0;
|
|
||||||
foreach (var group in input.Groups)
|
|
||||||
{
|
{
|
||||||
editGroupOrder++;
|
var field = new ListFormField();
|
||||||
var editOrder = 0;
|
fieldDto.ApplyTo(field);
|
||||||
foreach (var item in group.Items)
|
await repoListFormField.InsertAsync(field, autoSave: true);
|
||||||
{
|
}
|
||||||
fieldOrder++;
|
|
||||||
if (item.IncludeInEditingForm && item.FieldName != input.KeyFieldName)
|
|
||||||
{
|
|
||||||
editOrder++;
|
|
||||||
}
|
|
||||||
await repoListFormField.InsertAsync(new ListFormField
|
|
||||||
{
|
|
||||||
ListFormCode = input.ListFormCode,
|
|
||||||
FieldName = item.FieldName,
|
|
||||||
CaptionName = item.CaptionName,
|
|
||||||
Visible = item.FieldName != input.KeyFieldName,
|
|
||||||
IsActive = true,
|
|
||||||
AllowSearch = true,
|
|
||||||
Width = 0,
|
|
||||||
ListOrderNo = fieldOrder,
|
|
||||||
EditGroupOrderNo = item.IncludeInEditingForm ? editGroupOrder : null,
|
|
||||||
EditOrderNo = item.IncludeInEditingForm && item.FieldName != input.KeyFieldName
|
|
||||||
? editOrder
|
|
||||||
: null,
|
|
||||||
EditorType2 = item.EditorType,
|
|
||||||
ColSpan = item.ColSpan,
|
|
||||||
EditorOptions = string.IsNullOrWhiteSpace(item.EditorOptions) ? null : item.EditorOptions,
|
|
||||||
EditorScript = string.IsNullOrWhiteSpace(item.EditorScript) ? null : item.EditorScript,
|
|
||||||
SourceDbType = item.DbSourceType,
|
|
||||||
CultureName = PlatformConsts.DefaultLanguage,
|
|
||||||
PermissionJson = WizardConsts.DefaultFieldPermissionJson(code),
|
|
||||||
ColumnCustomizationJson = WizardConsts.DefaultColumnCustomizationJson,
|
|
||||||
ColumnFilterJson = WizardConsts.DefaultColumnFilteringJson,
|
|
||||||
PivotSettingsJson = WizardConsts.DefaultPivotSettingsJson,
|
|
||||||
LookupJson = !string.IsNullOrWhiteSpace(item.LookupQuery)
|
|
||||||
? WizardConsts.DefaultLookupJson(item.LookupDataSourceType, item.DisplayExpr, item.ValueExpr, item.LookupQuery)
|
|
||||||
: null,
|
|
||||||
}, autoSave: true);
|
|
||||||
|
|
||||||
await CreateLangKey(item.CaptionName, item.EnglishCaption, item.TurkishCaption, inserted);
|
foreach (var item in input.Groups.SelectMany(g => g.Items))
|
||||||
}
|
{
|
||||||
|
await CreateLangKey(item.CaptionName, item.EnglishCaption, item.TurkishCaption, inserted);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var criteria in workflow.Criteria)
|
foreach (var criteria in workflow.Criteria)
|
||||||
|
|
@ -496,153 +393,48 @@ public class ListFormWizardAppService(
|
||||||
await _languageTextAppService.ClearRedisCacheAsync();
|
await _languageTextAppService.ClearRedisCacheAsync();
|
||||||
|
|
||||||
// Wizard konfigürasyonunu seed dosyasına kaydet
|
// Wizard konfigürasyonunu seed dosyasına kaydet
|
||||||
await SaveWizardSeedFileAsync(input, isDeleted, isCreated, inserted);
|
await SaveWizardSeedFileAsync(input, listFormDto, fieldDtos, isDeleted, isCreated, inserted);
|
||||||
}
|
|
||||||
|
|
||||||
private static bool HasWorkflow(WorkflowDto workflow)
|
|
||||||
{
|
|
||||||
return workflow != null && (
|
|
||||||
!string.IsNullOrWhiteSpace(workflow.ApprovalUserFieldName) ||
|
|
||||||
!string.IsNullOrWhiteSpace(workflow.ApprovalStatusFieldName) ||
|
|
||||||
workflow.Criteria.Count > 0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EnsureUniqueWorkflowCriteriaTitles(List<ListFormWorkflowCriteriaDto> criteria)
|
|
||||||
{
|
|
||||||
if (criteria == null || criteria.Count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var baseTitles = criteria
|
|
||||||
.Select(x => string.IsNullOrWhiteSpace(x.Title) ? NormalizeWorkflowTitleFallback(x.Kind) : x.Title.Trim())
|
|
||||||
.ToList();
|
|
||||||
var duplicateTitles = baseTitles
|
|
||||||
.GroupBy(x => x, StringComparer.OrdinalIgnoreCase)
|
|
||||||
.Where(x => x.Count() > 1)
|
|
||||||
.Select(x => x.Key)
|
|
||||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
||||||
var titleCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
var usedTitles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
foreach (var item in criteria)
|
|
||||||
{
|
|
||||||
var baseTitle = string.IsNullOrWhiteSpace(item.Title) ? NormalizeWorkflowTitleFallback(item.Kind) : item.Title.Trim();
|
|
||||||
var title = baseTitle;
|
|
||||||
|
|
||||||
if (duplicateTitles.Contains(baseTitle))
|
|
||||||
{
|
|
||||||
titleCounts.TryGetValue(baseTitle, out var count);
|
|
||||||
count++;
|
|
||||||
titleCounts[baseTitle] = count;
|
|
||||||
title = $"{baseTitle}{count}";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (usedTitles.Contains(title))
|
|
||||||
{
|
|
||||||
var index = 1;
|
|
||||||
var candidate = $"{title}{index}";
|
|
||||||
while (usedTitles.Contains(candidate))
|
|
||||||
{
|
|
||||||
index++;
|
|
||||||
candidate = $"{title}{index}";
|
|
||||||
}
|
|
||||||
|
|
||||||
title = candidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
item.Title = title;
|
|
||||||
usedTitles.Add(title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string NormalizeWorkflowTitleFallback(string kind)
|
|
||||||
{
|
|
||||||
return string.IsNullOrWhiteSpace(kind) ? "Step" : kind.Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void NormalizeWorkflowCriteriaIds(string listFormCode, List<ListFormWorkflowCriteriaDto> criteria)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(listFormCode) || criteria == null || criteria.Count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var item in criteria)
|
|
||||||
{
|
|
||||||
item.Id = NormalizeWorkflowCriteriaId(listFormCode, item.Id);
|
|
||||||
item.ListFormCode = listFormCode;
|
|
||||||
item.NextOnStart = NormalizeWorkflowTargetId(listFormCode, item.NextOnStart);
|
|
||||||
item.NextOnTrue = NormalizeWorkflowTargetId(listFormCode, item.NextOnTrue);
|
|
||||||
item.NextOnFalse = NormalizeWorkflowTargetId(listFormCode, item.NextOnFalse);
|
|
||||||
item.NextOnApprove = NormalizeWorkflowTargetId(listFormCode, item.NextOnApprove);
|
|
||||||
item.NextOnReject = NormalizeWorkflowTargetId(listFormCode, item.NextOnReject);
|
|
||||||
|
|
||||||
foreach (var outcome in item.CompareOutcomes ?? [])
|
|
||||||
{
|
|
||||||
outcome.TargetId = NormalizeWorkflowTargetId(listFormCode, outcome.TargetId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string NormalizeWorkflowTargetId(string listFormCode, string id)
|
|
||||||
{
|
|
||||||
return string.IsNullOrWhiteSpace(id)
|
|
||||||
? string.Empty
|
|
||||||
: NormalizeWorkflowCriteriaId(listFormCode, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string NormalizeWorkflowCriteriaId(string listFormCode, string id)
|
|
||||||
{
|
|
||||||
var trimmed = id?.Trim();
|
|
||||||
if (string.IsNullOrWhiteSpace(trimmed))
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trimmed.StartsWith($"{listFormCode}-", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return trimmed;
|
|
||||||
}
|
|
||||||
|
|
||||||
return trimmed.StartsWith("N", StringComparison.OrdinalIgnoreCase)
|
|
||||||
? $"{listFormCode}-{trimmed}"
|
|
||||||
: trimmed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Wizard konfigürasyonunu JSON dosyası olarak kaydeder.
|
/// Wizard konfigürasyonunu JSON dosyası olarak kaydeder.
|
||||||
/// Dosya, seeder'lar ve File Manager ile ortak olan seed klasörünün wizard dizinine yazılır.
|
/// Dosya, seeder'lar ve File Manager ile ortak olan seed klasörünün wizard dizinine yazılır.
|
||||||
/// Veritabanı silinip yeniden oluşturulduğunda WizardDataSeeder bu dosyaları okuyarak konfigürasyonu geri yükler.
|
/// Veritabanı silinip yeniden oluşturulduğunda WizardDataSeeder bu dosyaları okuyarak konfigürasyonu geri yükler.
|
||||||
|
/// <para>
|
||||||
|
/// Dosya yalnizca wizard'in cevaplarini degil, urettigi ekranin ListForm sozlesmesini de tasir;
|
||||||
|
/// tasarimci uzerinde yapilan degisiklikler ayni bolumlere geri yazildigi icin dosya her zaman
|
||||||
|
/// ekranin guncel halidir.
|
||||||
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task SaveWizardSeedFileAsync(ListFormWizardDto input, bool isDeletedField, bool isCreatedField, WizardInsertedRecordsDto inserted)
|
private async Task SaveWizardSeedFileAsync(
|
||||||
|
ListFormWizardDto input,
|
||||||
|
GridOptionsEditDto listForm,
|
||||||
|
List<ColumnFormatEditDto> fields,
|
||||||
|
bool isDeletedField,
|
||||||
|
bool isCreatedField,
|
||||||
|
WizardInsertedRecordsDto inserted)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var outputPath = ResolveWizardSeedOutputPath();
|
var outputPath = ResolveWizardSeedOutputPath();
|
||||||
Directory.CreateDirectory(outputPath);
|
Directory.CreateDirectory(outputPath);
|
||||||
|
|
||||||
// Duzenleme bilgisi yalnizca istege aittir; seed dosyasinda yeri yoktur.
|
// Duzenlemede ilk deploy zamani korunur; seeder dosyalari bu damgaya gore siralar.
|
||||||
input.EditFileName = null;
|
if (input.SeededAt == default)
|
||||||
|
input.SeededAt = Clock.Now;
|
||||||
|
|
||||||
var seedData = new WizardSeedFileDto
|
var seedData = new WizardSeedFileDto
|
||||||
{
|
{
|
||||||
Wizard = input,
|
Wizard = input,
|
||||||
|
ListForm = listForm,
|
||||||
|
Fields = fields ?? [],
|
||||||
IsDeletedField = isDeletedField,
|
IsDeletedField = isDeletedField,
|
||||||
IsCreatedField = isCreatedField,
|
IsCreatedField = isCreatedField,
|
||||||
InsertedRecords = inserted
|
InsertedRecords = inserted
|
||||||
};
|
};
|
||||||
|
|
||||||
var json = JsonSerializer.Serialize(seedData, SeedJsonWriteOptions);
|
var filePath = Path.Combine(outputPath, ResolveWizardSeedFileName(input.WizardName));
|
||||||
// Dosya adı kullanıcı girdisinden türetildiği için geçersiz karakterler ayıklanır.
|
await File.WriteAllTextAsync(filePath, seedData.ToSeedJson());
|
||||||
var safeWizardName = string.Concat(input.WizardName.Trim().Split(Path.GetInvalidFileNameChars()));
|
|
||||||
if (string.IsNullOrWhiteSpace(safeWizardName))
|
|
||||||
safeWizardName = "Wizard";
|
|
||||||
|
|
||||||
var timestamp = Clock.Now.ToString("yyyyMMddHHmmss");
|
|
||||||
var filePath = Path.Combine(outputPath, $"{timestamp}_{safeWizardName}.json");
|
|
||||||
await File.WriteAllTextAsync(filePath, json);
|
|
||||||
|
|
||||||
logger.LogInformation("Seed file saved: {FilePath}", filePath);
|
logger.LogInformation("Seed file saved: {FilePath}", filePath);
|
||||||
}
|
}
|
||||||
|
|
@ -653,6 +445,38 @@ public class ListFormWizardAppService(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Duzenlenen dosyanin ilk deploy zamanini okur; dosya okunamazsa varsayilan deger doner ve
|
||||||
|
/// kayit sirasinda yeni damga atilir.
|
||||||
|
/// </summary>
|
||||||
|
private async Task<DateTime> ReadSeededAtAsync(string fileName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = await File.ReadAllTextAsync(ResolveWizardSeedFilePath(fileName));
|
||||||
|
var seed = WizardSeedFileDto.FromSeedJson(json);
|
||||||
|
return seed?.Wizard?.SeededAt ?? default;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogWarning(ex, "Wizard seed file could not be read for SeededAt: {FileName}", fileName);
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Seed dosyasinin adi wizard adiyla aynidir; dosya adi tarih tasimaz. Ad kullanici
|
||||||
|
/// girdisinden turedigi icin gecersiz karakterler ayiklanir.
|
||||||
|
/// </summary>
|
||||||
|
private static string ResolveWizardSeedFileName(string wizardName)
|
||||||
|
{
|
||||||
|
var safeWizardName = string.Concat((wizardName ?? string.Empty).Trim().Split(Path.GetInvalidFileNameChars()));
|
||||||
|
if (string.IsNullOrWhiteSpace(safeWizardName))
|
||||||
|
safeWizardName = "Wizard";
|
||||||
|
|
||||||
|
return $"{safeWizardName}.json";
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Custom yolunda component adı ve rota zorunludur. Rota <see cref="ListFormWizardDto.MenuUrl"/>
|
/// Custom yolunda component adı ve rota zorunludur. Rota <see cref="ListFormWizardDto.MenuUrl"/>
|
||||||
/// üzerinden taşınır ve CustomComponent sözleşmesiyle (<c>CreateUpdateCustomComponentDto.RoutePath</c>)
|
/// üzerinden taşınır ve CustomComponent sözleşmesiyle (<c>CreateUpdateCustomComponentDto.RoutePath</c>)
|
||||||
|
|
@ -724,7 +548,7 @@ public class ListFormWizardAppService(
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var json = await File.ReadAllTextAsync(file);
|
var json = await File.ReadAllTextAsync(file);
|
||||||
var seed = JsonSerializer.Deserialize<WizardSeedFileDto>(json, SeedJsonReadOptions);
|
var seed = WizardSeedFileDto.FromSeedJson(json);
|
||||||
result.Add(new WizardFileInfoDto
|
result.Add(new WizardFileInfoDto
|
||||||
{
|
{
|
||||||
FileName = fileName,
|
FileName = fileName,
|
||||||
|
|
@ -735,7 +559,7 @@ public class ListFormWizardAppService(
|
||||||
ComponentKind = seed?.Wizard?.ComponentKind ?? WizardComponentKindEnum.List,
|
ComponentKind = seed?.Wizard?.ComponentKind ?? WizardComponentKindEnum.List,
|
||||||
CustomComponentName = seed?.Wizard?.CustomComponentName ?? string.Empty,
|
CustomComponentName = seed?.Wizard?.CustomComponentName ?? string.Empty,
|
||||||
MenuUrl = seed?.Wizard?.MenuUrl ?? string.Empty,
|
MenuUrl = seed?.Wizard?.MenuUrl ?? string.Empty,
|
||||||
CreatedAt = fileName.Length >= 12 ? fileName[..12] : fileName,
|
CreatedAt = seed?.Wizard?.SeededAt.ToString("s") ?? string.Empty,
|
||||||
HasInsertedRecords = seed?.InsertedRecords != null &&
|
HasInsertedRecords = seed?.InsertedRecords != null &&
|
||||||
(seed.InsertedRecords.LanguageKeys.Count > 0 ||
|
(seed.InsertedRecords.LanguageKeys.Count > 0 ||
|
||||||
seed.InsertedRecords.PermissionGroupNames.Count > 0 ||
|
seed.InsertedRecords.PermissionGroupNames.Count > 0 ||
|
||||||
|
|
@ -759,7 +583,7 @@ public class ListFormWizardAppService(
|
||||||
var filePath = ResolveWizardSeedFilePath(fileName);
|
var filePath = ResolveWizardSeedFilePath(fileName);
|
||||||
|
|
||||||
var json = await File.ReadAllTextAsync(filePath);
|
var json = await File.ReadAllTextAsync(filePath);
|
||||||
var seed = JsonSerializer.Deserialize<WizardSeedFileDto>(json, SeedJsonReadOptions);
|
var seed = WizardSeedFileDto.FromSeedJson(json);
|
||||||
return seed ?? throw new UserFriendlyException("Dosya okunamadı.");
|
return seed ?? throw new UserFriendlyException("Dosya okunamadı.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -779,7 +603,7 @@ public class ListFormWizardAppService(
|
||||||
var filePath = ResolveWizardSeedFilePath(fileName);
|
var filePath = ResolveWizardSeedFilePath(fileName);
|
||||||
|
|
||||||
var json = await File.ReadAllTextAsync(filePath);
|
var json = await File.ReadAllTextAsync(filePath);
|
||||||
var seed = JsonSerializer.Deserialize<WizardSeedFileDto>(json, SeedJsonReadOptions);
|
var seed = WizardSeedFileDto.FromSeedJson(json);
|
||||||
|
|
||||||
if (seed != null)
|
if (seed != null)
|
||||||
await DeleteWizardDataAsync(seed);
|
await DeleteWizardDataAsync(seed);
|
||||||
|
|
@ -992,7 +816,7 @@ public class ListFormWizardAppService(
|
||||||
{
|
{
|
||||||
var filePath = ResolveWizardSeedFilePath(fileName);
|
var filePath = ResolveWizardSeedFilePath(fileName);
|
||||||
var json = await File.ReadAllTextAsync(filePath);
|
var json = await File.ReadAllTextAsync(filePath);
|
||||||
var seed = JsonSerializer.Deserialize<WizardSeedFileDto>(json, SeedJsonReadOptions)
|
var seed = WizardSeedFileDto.FromSeedJson(json)
|
||||||
?? throw new UserFriendlyException("Dosya okunamadı.");
|
?? throw new UserFriendlyException("Dosya okunamadı.");
|
||||||
|
|
||||||
var scopeRoot = Path.GetFullPath(SeedPathResolver.GetScopePath(_configuration, CurrentTenant.Id));
|
var scopeRoot = Path.GetFullPath(SeedPathResolver.GetScopePath(_configuration, CurrentTenant.Id));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Sozsoft.Platform.Data.Seeds;
|
||||||
|
using Sozsoft.Platform.Entities;
|
||||||
|
using Volo.Abp.DependencyInjection;
|
||||||
|
using Volo.Abp.Domain.Repositories;
|
||||||
|
using Volo.Abp.MultiTenancy;
|
||||||
|
using Volo.Abp.ObjectMapping;
|
||||||
|
|
||||||
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ListForm tasarimcisinda yapilan degisiklikleri wizard seed dosyasina geri yazar.
|
||||||
|
/// </summary>
|
||||||
|
public interface IWizardSeedSynchronizer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Verilen ListForm koduna ait bir wizard seed dosyasi varsa dosyanin ekran ve alan
|
||||||
|
/// bolumlerini veritabanindaki guncel haliyle degistirir. Dosya yoksa hicbir sey yapmaz.
|
||||||
|
/// </summary>
|
||||||
|
Task SyncAsync(string listFormCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wizard ile uretilen bir ekran, sonrasinda ListForm detayindan duzenlenebilir. Bu iki yol
|
||||||
|
/// ayrisirsa wizard yeniden deploy edildiginde tasarimcida yapilan is kaybolur; bu nedenle
|
||||||
|
/// ListForm ve alan kayitlarinin her degisiminde seed dosyasi da guncellenir.
|
||||||
|
/// </summary>
|
||||||
|
public class WizardSeedSynchronizer(
|
||||||
|
IRepository<ListForm, Guid> repoListForm,
|
||||||
|
IRepository<ListFormField, Guid> repoListFormField,
|
||||||
|
IObjectMapper objectMapper,
|
||||||
|
IConfiguration configuration,
|
||||||
|
ICurrentTenant currentTenant,
|
||||||
|
ILogger<WizardSeedSynchronizer> logger) : IWizardSeedSynchronizer, ITransientDependency
|
||||||
|
{
|
||||||
|
|
||||||
|
public async Task SyncAsync(string listFormCode)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(listFormCode))
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var (filePath, seed) = await FindSeedFileAsync(listFormCode);
|
||||||
|
if (seed == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var listForm = await repoListForm.FirstOrDefaultAsync(a => a.ListFormCode == listFormCode);
|
||||||
|
if (listForm == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var fields = await repoListFormField.GetListAsync(a => a.ListFormCode == listFormCode);
|
||||||
|
|
||||||
|
seed.ListForm = objectMapper.Map<ListForm, GridOptionsEditDto>(listForm);
|
||||||
|
seed.Fields = [.. fields
|
||||||
|
.OrderBy(f => f.ListOrderNo)
|
||||||
|
.Select(objectMapper.Map<ListFormField, ColumnFormatEditDto>)];
|
||||||
|
|
||||||
|
// Wizard bolumu de guncellenir; aksi halde ayni wizard yeniden deploy edildiginde
|
||||||
|
// tasarimcidaki degisiklikler eski cevaplarla ezilirdi.
|
||||||
|
seed.Wizard.RefreshFrom(seed.ListForm, seed.Fields);
|
||||||
|
|
||||||
|
await File.WriteAllTextAsync(filePath, seed.ToSeedJson());
|
||||||
|
logger.LogInformation("Wizard seed file synchronized: {FilePath}", filePath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Senkronizasyon hatasi tasarimcidaki kaydetme islemini engellemez.
|
||||||
|
logger.LogError(ex, "Wizard seed file could not be synchronized for '{ListFormCode}': {Message}", listFormCode, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Kapsam klasorundeki wizard dosyalari icinde bu ListForm koduna ait olani bulur. Dosya adi
|
||||||
|
/// wizard adiyla ayni oldugu icin once dogrudan ad denenir; eski ya da elle adlandirilmis
|
||||||
|
/// dosyalar icin klasor taranir.
|
||||||
|
/// </summary>
|
||||||
|
private async Task<(string FilePath, WizardSeedFileDto Seed)> FindSeedFileAsync(string listFormCode)
|
||||||
|
{
|
||||||
|
var wizardPath = SeedPathResolver.GetScopePath(configuration, currentTenant.Id, SeedPathResolver.WizardFolder);
|
||||||
|
if (!Directory.Exists(wizardPath))
|
||||||
|
return (null, null);
|
||||||
|
|
||||||
|
var candidates = new List<string>();
|
||||||
|
var directPath = Path.Combine(wizardPath, $"{listFormCode}.json");
|
||||||
|
if (File.Exists(directPath))
|
||||||
|
candidates.Add(directPath);
|
||||||
|
|
||||||
|
candidates.AddRange(Directory
|
||||||
|
.GetFiles(wizardPath, "*.json")
|
||||||
|
.Where(f => !string.Equals(f, directPath, StringComparison.OrdinalIgnoreCase)));
|
||||||
|
|
||||||
|
foreach (var candidate in candidates)
|
||||||
|
{
|
||||||
|
var seed = await ReadSeedAsync(candidate);
|
||||||
|
if (seed?.Wizard != null &&
|
||||||
|
string.Equals(seed.Wizard.ListFormCode, listFormCode, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return (candidate, seed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<WizardSeedFileDto> ReadSeedAsync(string filePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return WizardSeedFileDto.FromSeedJson(await File.ReadAllTextAsync(filePath));
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
logger.LogWarning(ex, "Wizard seed file could not be parsed: {FilePath}", filePath);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17190,6 +17190,18 @@
|
||||||
"en": "Select at least one operation or turn generation off",
|
"en": "Select at least one operation or turn generation off",
|
||||||
"tr": "En az bir operasyon seçin ya da üretimi kapatın"
|
"tr": "En az bir operasyon seçin ya da üretimi kapatın"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SqlQueryManager.ExistingCrudEndpoints",
|
||||||
|
"en": "Existing CRUD endpoints",
|
||||||
|
"tr": "Kayıtlı CRUD endpoint'leri"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SqlQueryManager.ExistingCrudEndpointsHint",
|
||||||
|
"en": "This table already has generated endpoints; deploying again regenerates them with the operations selected below.",
|
||||||
|
"tr": "Bu tablo için endpoint'ler daha önce üretilmiş; tekrar deploy edildiğinde aşağıda seçilen operasyonlarla yeniden üretilir."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
"key": "App.SqlQueryManager.CrudEndpointsInactiveNote",
|
"key": "App.SqlQueryManager.CrudEndpointsInactiveNote",
|
||||||
|
|
|
||||||
|
|
@ -84,30 +84,44 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var jsonFiles = Directory.GetFiles(wizardDataPath, "*.json").OrderBy(f => Path.GetFileName(f)).ToArray();
|
var jsonFiles = Directory.GetFiles(wizardDataPath, "*.json");
|
||||||
if (jsonFiles.Length == 0)
|
if (jsonFiles.Length == 0)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("No JSON files found in Seeds/{Scope}/wizard directory, skipping.", scopeFolderName);
|
_logger.LogInformation("No JSON files found in Seeds/{Scope}/wizard directory, skipping.", scopeFolderName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("WizardDataSeeder started. {Count} files to be processed.", jsonFiles.Length);
|
// Dosya adi tarih tasimadigi icin uygulama sirasi dosyanin kendi damgasindan gelir;
|
||||||
|
// birbirine bagli wizard'lar (once Customers, sonra Tasks) uretildikleri sirayla uygulanir.
|
||||||
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
var seedFiles = new List<(string FilePath, WizardSeedFileDto Seed)>();
|
||||||
|
|
||||||
foreach (var filePath in jsonFiles)
|
foreach (var filePath in jsonFiles)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var json = await File.ReadAllTextAsync(filePath);
|
var seed = WizardSeedFileDto.FromSeedJson(await File.ReadAllTextAsync(filePath));
|
||||||
var seedFile = JsonSerializer.Deserialize<WizardSeedFileDto>(json, options);
|
if (seed?.Wizard == null)
|
||||||
|
|
||||||
if (seedFile?.Wizard == null)
|
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Invalid file skipped: {FilePath}", filePath);
|
_logger.LogWarning("Invalid file skipped: {FilePath}", filePath);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
seedFiles.Add((filePath, seed));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, $"Error - {filePath}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("WizardDataSeeder started. {Count} files to be processed.", seedFiles.Count);
|
||||||
|
|
||||||
|
foreach (var (filePath, seedFile) in seedFiles
|
||||||
|
.OrderBy(x => x.Seed.Wizard.SeededAt)
|
||||||
|
.ThenBy(x => Path.GetFileName(x.FilePath), StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var fileName = Path.GetFileName(filePath);
|
||||||
|
try
|
||||||
|
{
|
||||||
var wizardName = seedFile.Wizard.WizardName?.Trim();
|
var wizardName = seedFile.Wizard.WizardName?.Trim();
|
||||||
if (string.IsNullOrWhiteSpace(wizardName))
|
if (string.IsNullOrWhiteSpace(wizardName))
|
||||||
{
|
{
|
||||||
|
|
@ -115,7 +129,12 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileName = Path.GetFileName(filePath);
|
var isCustomComponent = seedFile.Wizard.ComponentKind == WizardComponentKindEnum.Custom;
|
||||||
|
if (!isCustomComponent && seedFile.ListForm == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("[{File}] ListForm section is missing, skipped.", fileName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
await BackfillMenuParentModuleIdAsync(seedFile.Wizard);
|
await BackfillMenuParentModuleIdAsync(seedFile.Wizard);
|
||||||
|
|
||||||
|
|
@ -123,10 +142,10 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
// idempotency menü kaydı üzerinden değerlendirilir.
|
// idempotency menü kaydı üzerinden değerlendirilir.
|
||||||
// Menusuz Custom wizard'da ne ListForm ne de menu kaydi olustugu icin
|
// Menusuz Custom wizard'da ne ListForm ne de menu kaydi olustugu icin
|
||||||
// idempotency okuma permission'i uzerinden degerlendirilir.
|
// idempotency okuma permission'i uzerinden degerlendirilir.
|
||||||
var alreadySeeded = seedFile.Wizard.ComponentKind == WizardComponentKindEnum.Custom
|
var alreadySeeded = isCustomComponent
|
||||||
? seedFile.Wizard.CreateMenu
|
? seedFile.Wizard.CreateMenu
|
||||||
? await _repoMenu.AnyAsync(a => a.Code == ResolveMenuCode(seedFile.Wizard))
|
? await _repoMenu.AnyAsync(a => a.Code == seedFile.Wizard.ResolveMenuCode())
|
||||||
: await _repoPerm.AnyAsync(a => a.Name == ResolveMenuCode(seedFile.Wizard))
|
: await _repoPerm.AnyAsync(a => a.Name == seedFile.Wizard.ResolveMenuCode())
|
||||||
: await _repoListForm.AnyAsync(a => a.ListFormCode == seedFile.Wizard.ListFormCode);
|
: await _repoListForm.AnyAsync(a => a.ListFormCode == seedFile.Wizard.ListFormCode);
|
||||||
|
|
||||||
if (alreadySeeded)
|
if (alreadySeeded)
|
||||||
|
|
@ -149,23 +168,10 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
private async Task ApplyWizardSeedAsync(WizardSeedFileDto seedFile)
|
private async Task ApplyWizardSeedAsync(WizardSeedFileDto seedFile)
|
||||||
{
|
{
|
||||||
var input = seedFile.Wizard;
|
var input = seedFile.Wizard;
|
||||||
var isDeleted = seedFile.IsDeletedField;
|
input.Groups ??= [];
|
||||||
var isCreated = seedFile.IsCreatedField;
|
|
||||||
input.SubForms ??= new List<SubFormDto>();
|
|
||||||
input.Widgets ??= new List<WidgetEditDto>();
|
|
||||||
input.WorkflowDto ??= new WorkflowDto();
|
|
||||||
input.WorkflowDto.Criteria ??= [];
|
|
||||||
EnsureUniqueWorkflowCriteriaTitles(input.WorkflowDto.Criteria);
|
|
||||||
var workflow = input.WorkflowDto;
|
|
||||||
|
|
||||||
var wizardName = input.WizardName.Trim();
|
var code = input.ResolveMenuCode();
|
||||||
var code = string.IsNullOrWhiteSpace(input.MenuCode)
|
var listFormCode = input.ResolveListFormCode();
|
||||||
? WizardConsts.WizardKey(wizardName)
|
|
||||||
: input.MenuCode.Trim();
|
|
||||||
var listFormCode = string.IsNullOrWhiteSpace(input.ListFormCode)
|
|
||||||
? code
|
|
||||||
: input.ListFormCode.Trim();
|
|
||||||
NormalizeWorkflowCriteriaIds(listFormCode, workflow.Criteria);
|
|
||||||
var titleLangKey = $"{listFormCode}.Title";
|
var titleLangKey = $"{listFormCode}.Title";
|
||||||
var nameLangKey = code;
|
var nameLangKey = code;
|
||||||
var descLangKey = $"{listFormCode}.Desc";
|
var descLangKey = $"{listFormCode}.Desc";
|
||||||
|
|
@ -344,20 +350,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
}, autoSave: true);
|
}, autoSave: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditingFormJson
|
// ListForm ve alanlari - varsa sil, seed dosyasindaki sozlesmeden yeniden kur
|
||||||
var editingFormDtos = input.Groups
|
|
||||||
.Select((g, gi) => new EditingFormDto
|
|
||||||
{
|
|
||||||
Order = gi + 1,
|
|
||||||
Caption = g.Caption,
|
|
||||||
ColCount = g.ColCount,
|
|
||||||
ColSpan = g.ColCount,
|
|
||||||
ItemType = "group"
|
|
||||||
})
|
|
||||||
.Where((_, index) => input.Groups[index].Items.Any(i => i.IncludeInEditingForm && i.FieldName != input.KeyFieldName))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
// ListForm - varsa sil, yeniden ekle
|
|
||||||
var existingListForm = await _repoListForm.FirstOrDefaultAsync(a => a.ListFormCode == input.ListFormCode);
|
var existingListForm = await _repoListForm.FirstOrDefaultAsync(a => a.ListFormCode == input.ListFormCode);
|
||||||
if (existingListForm != null)
|
if (existingListForm != null)
|
||||||
{
|
{
|
||||||
|
|
@ -376,170 +369,26 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
await _repoListFormWorkflow.DeleteManyAsync(existingWorkflowCriteria, autoSave: true);
|
await _repoListFormWorkflow.DeleteManyAsync(existingWorkflowCriteria, autoSave: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditingForm'da required olan alanları tespit et, ValidationRuleJson eklemek için
|
// Ekran, seed dosyasinin tasidigi ListForm sozlesmesinden kurulur; boylece wizard
|
||||||
var editingFormFieldNames = input.Groups
|
// sonrasinda tasarimci uzerinde yapilan degisiklikler de geri gelir.
|
||||||
.SelectMany(g => g.Items)
|
var listForm = new ListForm();
|
||||||
.Where(i => i.IncludeInEditingForm && i.FieldName != input.KeyFieldName)
|
seedFile.ListForm.ApplyTo(listForm);
|
||||||
.Select(i => new { FieldName = i.FieldName, Isrequired = i.IsRequired })
|
await _repoListForm.InsertAsync(listForm, autoSave: true);
|
||||||
.ToList();
|
|
||||||
|
|
||||||
// Heigth ve Width hesaplama
|
foreach (var fieldDto in seedFile.Fields)
|
||||||
var groupLayouts = input.Groups
|
|
||||||
.Select(g =>
|
|
||||||
{
|
|
||||||
var items = g.Items
|
|
||||||
.Where(i => i.IncludeInEditingForm && i.FieldName != input.KeyFieldName)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var columnCount = g.ColCount;
|
|
||||||
|
|
||||||
if (columnCount <= 0)
|
|
||||||
columnCount = 1;
|
|
||||||
|
|
||||||
// 3 kolonu geçmesin
|
|
||||||
columnCount = Math.Min(columnCount, WizardConsts.EditFormMaxColumnCount);
|
|
||||||
|
|
||||||
// Örneğin 4 item / 3 kolon = 2 satır
|
|
||||||
var rowCount = (int)Math.Ceiling(items.Count / (double)columnCount);
|
|
||||||
|
|
||||||
return new
|
|
||||||
{
|
|
||||||
Group = g,
|
|
||||||
Items = items,
|
|
||||||
ColumnCount = columnCount,
|
|
||||||
RowCount = rowCount
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
// Form genelinde kullanılacak kolon sayısı
|
|
||||||
var formColumnCount = groupLayouts.Any()
|
|
||||||
? groupLayouts.Max(x => x.ColumnCount)
|
|
||||||
: 1;
|
|
||||||
|
|
||||||
// Width hesabı
|
|
||||||
var formWidth = (WizardConsts.EditFormMaxWidth / WizardConsts.EditFormMaxColumnCount) * formColumnCount;
|
|
||||||
|
|
||||||
// Toplam satır sayısı
|
|
||||||
var totalRowCount = groupLayouts.Sum(x => x.RowCount);
|
|
||||||
|
|
||||||
// Height hesabı
|
|
||||||
var formHeight = WizardConsts.EditFormHeightDefault + (totalRowCount * WizardConsts.EditFormHeightByRow);
|
|
||||||
|
|
||||||
// ListForm
|
|
||||||
await _repoListForm.InsertAsync(new ListForm
|
|
||||||
{
|
{
|
||||||
ListFormType = ListFormTypeEnum.List,
|
var field = new ListFormField();
|
||||||
PageSize = 10,
|
fieldDto.ApplyTo(field);
|
||||||
ExportJson = WizardConsts.DefaultExportJson,
|
await _repoListFormField.InsertAsync(field, autoSave: true);
|
||||||
IsSubForm = false,
|
|
||||||
ShowNote = input.SubForms.Count > 0 || workflow.Criteria.Count > 0,
|
|
||||||
LayoutJson = WizardConsts.DefaultLayoutJson(input.DefaultLayout, input.Grid, input.Card, input.Pivot, input.Tree, input.Chart, input.Gantt, input.Scheduler, input.Todo),
|
|
||||||
CultureName = LanguageCodes.En,
|
|
||||||
ListFormCode = input.ListFormCode,
|
|
||||||
Name = nameLangKey,
|
|
||||||
Title = titleLangKey,
|
|
||||||
Description = descLangKey,
|
|
||||||
DataSourceCode = input.DataSourceCode,
|
|
||||||
IsTenant = input.IsTenant,
|
|
||||||
IsBranch = input.IsBranch,
|
|
||||||
IsOrganizationUnit = input.IsOrganizationUnit,
|
|
||||||
SelectCommandType = input.SelectCommandType,
|
|
||||||
SelectCommand = input.SelectCommand,
|
|
||||||
KeyFieldName = input.KeyFieldName,
|
|
||||||
KeyFieldDbSourceType = input.KeyFieldDbSourceType,
|
|
||||||
DefaultFilter = isDeleted ? WizardConsts.DefaultFilterJson : null,
|
|
||||||
SortMode = GridOptions.SortModeSingle,
|
|
||||||
FilterRowJson = WizardConsts.DefaultFilterRowJson(),
|
|
||||||
HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson(),
|
|
||||||
SearchPanelJson = WizardConsts.DefaultSearchPanelJson(),
|
|
||||||
GroupPanelJson = WizardConsts.DefaultGroupPanelJson(),
|
|
||||||
SelectionJson = WizardConsts.DefaultSelectionSingleJson(workflow.Criteria.Count > 0 ? GridOptions.SelectionModeSingle : GridOptions.SelectionModeNone),
|
|
||||||
ColumnOptionJson = WizardConsts.DefaultColumnOptionJson(),
|
|
||||||
PermissionJson = WizardConsts.DefaultPermissionJson(code),
|
|
||||||
DeleteCommand = isDeleted ? WizardConsts.DefaultDeleteCommand(input.SelectCommand) : null,
|
|
||||||
DeleteFieldsDefaultValueJson = isDeleted
|
|
||||||
? WizardConsts.DefaultDeleteFieldsDefaultValueJson(input.KeyFieldDbSourceType)
|
|
||||||
: WizardConsts.DefaultDeleteFieldsJsonOnlyId(input.KeyFieldDbSourceType),
|
|
||||||
InsertFieldsDefaultValueJson = isCreated
|
|
||||||
? WizardConsts.DefaultInsertFieldsDefaultValueJson(input.KeyFieldDbSourceType)
|
|
||||||
: WizardConsts.DefaultInsertFieldsJsonOnlyId(input.KeyFieldDbSourceType),
|
|
||||||
PagerOptionJson = WizardConsts.DefaultPagerOptionJson,
|
|
||||||
EditingOptionJson = WizardConsts.DefaultEditingOptionJson(titleLangKey, formWidth, formHeight, input.AllowDeleting, input.AllowAdding, input.AllowUpdating, input.ConfirmDelete, false, input.AllowDetail),
|
|
||||||
EditingFormJson = editingFormDtos.Count > 0 ? JsonSerializer.Serialize(editingFormDtos) : null,
|
|
||||||
SubFormsJson = input.SubForms.Count > 0 ? JsonSerializer.Serialize(input.SubForms) : null,
|
|
||||||
WidgetsJson = input.Widgets.Count > 0 ? JsonSerializer.Serialize(input.Widgets) : null,
|
|
||||||
WorkflowJson = HasWorkflow(workflow) ? JsonSerializer.Serialize(workflow) : null,
|
|
||||||
TreeOptionJson = (input.Tree || input.DefaultLayout == "tree") &&
|
|
||||||
!string.IsNullOrEmpty(input.TreeOptionDto?.ParentIdExpr)
|
|
||||||
? JsonSerializer.Serialize(input.TreeOptionDto)
|
|
||||||
: null,
|
|
||||||
GanttOptionJson = (input.Gantt || input.DefaultLayout == "gantt") &&
|
|
||||||
!string.IsNullOrEmpty(input.GanttOptionDto?.ParentIdExpr)
|
|
||||||
? JsonSerializer.Serialize(input.GanttOptionDto)
|
|
||||||
: null,
|
|
||||||
SchedulerOptionJson = (input.Scheduler || input.DefaultLayout == "scheduler") &&
|
|
||||||
!string.IsNullOrEmpty(input.SchedulerOptionDto?.TextExpr)
|
|
||||||
? JsonSerializer.Serialize(input.SchedulerOptionDto)
|
|
||||||
: null,
|
|
||||||
TodoOptionJson = (input.Todo || input.DefaultLayout == "todo") &&
|
|
||||||
!string.IsNullOrEmpty(input.TodoOptionDto?.TitleExpr) &&
|
|
||||||
!string.IsNullOrEmpty(input.TodoOptionDto?.StatusExpr)
|
|
||||||
? JsonSerializer.Serialize(input.TodoOptionDto)
|
|
||||||
: null,
|
|
||||||
}, autoSave: true);
|
|
||||||
|
|
||||||
// ListFormFields
|
|
||||||
var fieldOrder = 0;
|
|
||||||
var editGroupOrder = 0;
|
|
||||||
foreach (var group in input.Groups)
|
|
||||||
{
|
|
||||||
editGroupOrder++;
|
|
||||||
var editOrder = 0;
|
|
||||||
foreach (var item in group.Items)
|
|
||||||
{
|
|
||||||
fieldOrder++;
|
|
||||||
if (item.IncludeInEditingForm && item.FieldName != input.KeyFieldName)
|
|
||||||
{
|
|
||||||
editOrder++;
|
|
||||||
}
|
|
||||||
await _repoListFormField.InsertAsync(new ListFormField
|
|
||||||
{
|
|
||||||
ListFormCode = input.ListFormCode,
|
|
||||||
FieldName = item.FieldName,
|
|
||||||
CaptionName = item.CaptionName,
|
|
||||||
Visible = item.FieldName != input.KeyFieldName,
|
|
||||||
IsActive = true,
|
|
||||||
Width = 0, //Fit columns için hepsine sıfır veriyorum.
|
|
||||||
AllowSearch = true,
|
|
||||||
ListOrderNo = fieldOrder,
|
|
||||||
EditGroupOrderNo = item.IncludeInEditingForm ? editGroupOrder : null,
|
|
||||||
EditOrderNo = item.IncludeInEditingForm && item.FieldName != input.KeyFieldName
|
|
||||||
? editOrder
|
|
||||||
: null,
|
|
||||||
EditorType2 = item.EditorType,
|
|
||||||
ColSpan = item.ColSpan,
|
|
||||||
EditorOptions = string.IsNullOrWhiteSpace(item.EditorOptions) ? null : item.EditorOptions,
|
|
||||||
EditorScript = string.IsNullOrWhiteSpace(item.EditorScript) ? null : item.EditorScript,
|
|
||||||
SourceDbType = item.DbSourceType,
|
|
||||||
CultureName = PlatformConsts.DefaultLanguage,
|
|
||||||
PermissionJson = WizardConsts.DefaultFieldPermissionJson(code),
|
|
||||||
ColumnCustomizationJson = WizardConsts.DefaultColumnCustomizationJson,
|
|
||||||
ColumnFilterJson = WizardConsts.DefaultColumnFilteringJson,
|
|
||||||
PivotSettingsJson = WizardConsts.DefaultPivotSettingsJson,
|
|
||||||
LookupJson = !string.IsNullOrWhiteSpace(item.LookupQuery)
|
|
||||||
? WizardConsts.DefaultLookupJson(item.LookupDataSourceType, item.DisplayExpr, item.ValueExpr, item.LookupQuery)
|
|
||||||
: null,
|
|
||||||
ValidationRuleJson = editingFormFieldNames.Any(f => f.FieldName == item.FieldName && f.Isrequired)
|
|
||||||
? WizardConsts.DefaultValidationRuleRequiredJson
|
|
||||||
: null
|
|
||||||
}, autoSave: true);
|
|
||||||
|
|
||||||
await CreateLangKeyAsync(item.CaptionName, item.EnglishCaption, item.TurkishCaption);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var criteria in workflow.Criteria)
|
// Alan basliklarinin iki dilli metinleri yalnizca wizard bolumunde tasinir.
|
||||||
|
foreach (var item in input.Groups.SelectMany(g => g.Items))
|
||||||
|
{
|
||||||
|
await CreateLangKeyAsync(item.CaptionName, item.EnglishCaption, item.TurkishCaption);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var criteria in seedFile.ListForm.WorkflowDto.Criteria)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(criteria.Id))
|
if (string.IsNullOrWhiteSpace(criteria.Id))
|
||||||
{
|
{
|
||||||
|
|
@ -568,17 +417,6 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Wizard'in menu kodunu, ApplyWizardSeedAsync ile ayni kurala gore cozer.
|
|
||||||
/// Custom yolunda idempotency kontrolu ListForm yerine bu kod uzerinden yapilir.
|
|
||||||
/// </summary>
|
|
||||||
private static string ResolveMenuCode(ListFormWizardDto input)
|
|
||||||
{
|
|
||||||
return string.IsNullOrWhiteSpace(input.MenuCode)
|
|
||||||
? WizardConsts.WizardKey(input.WizardName.Trim())
|
|
||||||
: input.MenuCode.Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Custom yolunda menunun acacagi rotayi normalize eder. Rota MenuUrl uzerinden
|
/// Custom yolunda menunun acacagi rotayi normalize eder. Rota MenuUrl uzerinden
|
||||||
/// tasinir; bos ise menu hicbir yere gitmeyecegi icin seed dosyasi hatali kabul edilir.
|
/// tasinir; bos ise menu hicbir yere gitmeyecegi icin seed dosyasi hatali kabul edilir.
|
||||||
|
|
@ -647,119 +485,6 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
menuParent.ModuleId);
|
menuParent.ModuleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HasWorkflow(WorkflowDto workflow)
|
|
||||||
{
|
|
||||||
return workflow != null && (
|
|
||||||
!string.IsNullOrWhiteSpace(workflow.ApprovalUserFieldName) ||
|
|
||||||
!string.IsNullOrWhiteSpace(workflow.ApprovalDateFieldName) ||
|
|
||||||
!string.IsNullOrWhiteSpace(workflow.ApprovalStatusFieldName) ||
|
|
||||||
!string.IsNullOrWhiteSpace(workflow.ApprovalDescriptionFieldName) ||
|
|
||||||
workflow.Criteria.Count > 0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EnsureUniqueWorkflowCriteriaTitles(List<ListFormWorkflowCriteriaDto> criteria)
|
|
||||||
{
|
|
||||||
if (criteria == null || criteria.Count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var baseTitles = criteria
|
|
||||||
.Select(x => string.IsNullOrWhiteSpace(x.Title) ? NormalizeWorkflowTitleFallback(x.Kind) : x.Title.Trim())
|
|
||||||
.ToList();
|
|
||||||
var duplicateTitles = baseTitles
|
|
||||||
.GroupBy(x => x, StringComparer.OrdinalIgnoreCase)
|
|
||||||
.Where(x => x.Count() > 1)
|
|
||||||
.Select(x => x.Key)
|
|
||||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
||||||
var titleCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
var usedTitles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
foreach (var item in criteria)
|
|
||||||
{
|
|
||||||
var baseTitle = string.IsNullOrWhiteSpace(item.Title) ? NormalizeWorkflowTitleFallback(item.Kind) : item.Title.Trim();
|
|
||||||
var title = baseTitle;
|
|
||||||
|
|
||||||
if (duplicateTitles.Contains(baseTitle))
|
|
||||||
{
|
|
||||||
titleCounts.TryGetValue(baseTitle, out var count);
|
|
||||||
count++;
|
|
||||||
titleCounts[baseTitle] = count;
|
|
||||||
title = $"{baseTitle}{count}";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (usedTitles.Contains(title))
|
|
||||||
{
|
|
||||||
var index = 1;
|
|
||||||
var candidate = $"{title}{index}";
|
|
||||||
while (usedTitles.Contains(candidate))
|
|
||||||
{
|
|
||||||
index++;
|
|
||||||
candidate = $"{title}{index}";
|
|
||||||
}
|
|
||||||
|
|
||||||
title = candidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
item.Title = title;
|
|
||||||
usedTitles.Add(title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string NormalizeWorkflowTitleFallback(string kind)
|
|
||||||
{
|
|
||||||
return string.IsNullOrWhiteSpace(kind) ? "Step" : kind.Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void NormalizeWorkflowCriteriaIds(string listFormCode, List<ListFormWorkflowCriteriaDto> criteria)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(listFormCode) || criteria == null || criteria.Count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var item in criteria)
|
|
||||||
{
|
|
||||||
item.Id = NormalizeWorkflowCriteriaId(listFormCode, item.Id);
|
|
||||||
item.ListFormCode = listFormCode;
|
|
||||||
item.NextOnStart = NormalizeWorkflowTargetId(listFormCode, item.NextOnStart);
|
|
||||||
item.NextOnTrue = NormalizeWorkflowTargetId(listFormCode, item.NextOnTrue);
|
|
||||||
item.NextOnFalse = NormalizeWorkflowTargetId(listFormCode, item.NextOnFalse);
|
|
||||||
item.NextOnApprove = NormalizeWorkflowTargetId(listFormCode, item.NextOnApprove);
|
|
||||||
item.NextOnReject = NormalizeWorkflowTargetId(listFormCode, item.NextOnReject);
|
|
||||||
|
|
||||||
foreach (var outcome in item.CompareOutcomes ?? [])
|
|
||||||
{
|
|
||||||
outcome.TargetId = NormalizeWorkflowTargetId(listFormCode, outcome.TargetId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string NormalizeWorkflowTargetId(string listFormCode, string id)
|
|
||||||
{
|
|
||||||
return string.IsNullOrWhiteSpace(id)
|
|
||||||
? string.Empty
|
|
||||||
: NormalizeWorkflowCriteriaId(listFormCode, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string NormalizeWorkflowCriteriaId(string listFormCode, string id)
|
|
||||||
{
|
|
||||||
var trimmed = id?.Trim();
|
|
||||||
if (string.IsNullOrWhiteSpace(trimmed))
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trimmed.StartsWith($"{listFormCode}-", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return trimmed;
|
|
||||||
}
|
|
||||||
|
|
||||||
return trimmed.StartsWith("N", StringComparison.OrdinalIgnoreCase)
|
|
||||||
? $"{listFormCode}-{trimmed}"
|
|
||||||
: trimmed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task CreateLangKeyAsync(string key, string textEn, string textTr)
|
private async Task CreateLangKeyAsync(string key, string textEn, string textTr)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,157 @@
|
||||||
|
using System.Data;
|
||||||
|
using Sozsoft.Platform.Enums;
|
||||||
|
|
||||||
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bir ekranin (ListForm) yazilabilir kolon sozlesmesi. <c>ListForm</c> entity'si bu sozlesmeyi
|
||||||
|
/// uygular; boylece ekrani DTO'dan kuran cevirici, entity'yi tanimayan katmanlarda da (wizard
|
||||||
|
/// seed dosyasini isleyen DbMigrator gibi) tek bir yerde tutulabilir.
|
||||||
|
/// </summary>
|
||||||
|
public interface IListFormArtifact
|
||||||
|
{
|
||||||
|
string ListFormCode { get; set; }
|
||||||
|
string CultureName { get; set; }
|
||||||
|
string Name { get; set; }
|
||||||
|
string Title { get; set; }
|
||||||
|
string Description { get; set; }
|
||||||
|
string ListFormType { get; set; }
|
||||||
|
bool IsSubForm { get; set; }
|
||||||
|
string SubFormsListFormType { get; set; }
|
||||||
|
bool ShowNote { get; set; }
|
||||||
|
string SortMode { get; set; }
|
||||||
|
int PageSize { get; set; }
|
||||||
|
int? Width { get; set; }
|
||||||
|
int? Height { get; set; }
|
||||||
|
bool FullHeight { get; set; }
|
||||||
|
string UserId { get; set; }
|
||||||
|
string RoleId { get; set; }
|
||||||
|
|
||||||
|
string DataSourceCode { get; set; }
|
||||||
|
SelectCommandTypeEnum SelectCommandType { get; set; }
|
||||||
|
string SelectCommand { get; set; }
|
||||||
|
string TableName { get; set; }
|
||||||
|
string KeyFieldName { get; set; }
|
||||||
|
DbType KeyFieldDbSourceType { get; set; }
|
||||||
|
bool IsTenant { get; set; }
|
||||||
|
bool IsBranch { get; set; }
|
||||||
|
bool IsOrganizationUnit { get; set; }
|
||||||
|
string DefaultFilter { get; set; }
|
||||||
|
|
||||||
|
string InsertServiceAddress { get; set; }
|
||||||
|
string InsertCommand { get; set; }
|
||||||
|
string InsertBeforeCommand { get; set; }
|
||||||
|
string InsertAfterCommand { get; set; }
|
||||||
|
string UpdateServiceAddress { get; set; }
|
||||||
|
string UpdateCommand { get; set; }
|
||||||
|
string UpdateBeforeCommand { get; set; }
|
||||||
|
string UpdateAfterCommand { get; set; }
|
||||||
|
string DeleteServiceAddress { get; set; }
|
||||||
|
string DeleteCommand { get; set; }
|
||||||
|
string DeleteBeforeCommand { get; set; }
|
||||||
|
string DeleteAfterCommand { get; set; }
|
||||||
|
|
||||||
|
string SelectFieldsDefaultValueJson { get; set; }
|
||||||
|
string InsertFieldsDefaultValueJson { get; set; }
|
||||||
|
string UpdateFieldsDefaultValueJson { get; set; }
|
||||||
|
string DeleteFieldsDefaultValueJson { get; set; }
|
||||||
|
string FormFieldsDefaultValueJson { get; set; }
|
||||||
|
|
||||||
|
string LayoutJson { get; set; }
|
||||||
|
string FilterRowJson { get; set; }
|
||||||
|
string RowJson { get; set; }
|
||||||
|
string HeaderFilterJson { get; set; }
|
||||||
|
string FilterPanelJson { get; set; }
|
||||||
|
string SearchPanelJson { get; set; }
|
||||||
|
string GroupPanelJson { get; set; }
|
||||||
|
string SelectionJson { get; set; }
|
||||||
|
string ColumnOptionJson { get; set; }
|
||||||
|
string PivotOptionJson { get; set; }
|
||||||
|
string TreeOptionJson { get; set; }
|
||||||
|
string GanttOptionJson { get; set; }
|
||||||
|
string SchedulerOptionJson { get; set; }
|
||||||
|
string TodoOptionJson { get; set; }
|
||||||
|
string PagerOptionJson { get; set; }
|
||||||
|
string EditingOptionJson { get; set; }
|
||||||
|
string EditingFormJson { get; set; }
|
||||||
|
string PermissionJson { get; set; }
|
||||||
|
string CommandColumnJson { get; set; }
|
||||||
|
string StateStoringJson { get; set; }
|
||||||
|
string CustomJsSourcesJson { get; set; }
|
||||||
|
string CustomStyleSourcesJson { get; set; }
|
||||||
|
string SubFormsJson { get; set; }
|
||||||
|
string WidgetsJson { get; set; }
|
||||||
|
string WorkflowJson { get; set; }
|
||||||
|
string ExtraFilterJson { get; set; }
|
||||||
|
|
||||||
|
string CommonJson { get; set; }
|
||||||
|
string AdaptiveLayoutJson { get; set; }
|
||||||
|
string AnimationJson { get; set; }
|
||||||
|
string AnnotationsJson { get; set; }
|
||||||
|
string ArgumentAxisJson { get; set; }
|
||||||
|
string CommonAnnotationsSettingsJson { get; set; }
|
||||||
|
string CommonAxisSettingsJson { get; set; }
|
||||||
|
string CommonPaneSettingsJson { get; set; }
|
||||||
|
string CommonSeriesSettingsJson { get; set; }
|
||||||
|
string CrosshairJson { get; set; }
|
||||||
|
string ExportJson { get; set; }
|
||||||
|
string LegendJson { get; set; }
|
||||||
|
string MarginJson { get; set; }
|
||||||
|
string PanesJson { get; set; }
|
||||||
|
string ScrollBarJson { get; set; }
|
||||||
|
string SeriesJson { get; set; }
|
||||||
|
string SizeJson { get; set; }
|
||||||
|
string TitleJson { get; set; }
|
||||||
|
string TooltipJson { get; set; }
|
||||||
|
string ValueAxisJson { get; set; }
|
||||||
|
string ZoomAndPanJson { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bir ekran alaninin (ListFormField) yazilabilir kolon sozlesmesi;
|
||||||
|
/// <see cref="IListFormArtifact"/> ile ayni amaca hizmet eder.
|
||||||
|
/// </summary>
|
||||||
|
public interface IListFormFieldArtifact
|
||||||
|
{
|
||||||
|
string ListFormCode { get; set; }
|
||||||
|
string UserId { get; set; }
|
||||||
|
string RoleId { get; set; }
|
||||||
|
string CultureName { get; set; }
|
||||||
|
string FieldName { get; set; }
|
||||||
|
string CaptionName { get; set; }
|
||||||
|
string PlaceHolder { get; set; }
|
||||||
|
string BandName { get; set; }
|
||||||
|
bool? Visible { get; set; }
|
||||||
|
bool? IsActive { get; set; }
|
||||||
|
int? Width { get; set; }
|
||||||
|
int? ListOrderNo { get; set; }
|
||||||
|
DbType SourceDbType { get; set; }
|
||||||
|
int? SortIndex { get; set; }
|
||||||
|
string SortDirection { get; set; }
|
||||||
|
bool? AllowSearch { get; set; }
|
||||||
|
bool? AllowEditing { get; set; }
|
||||||
|
bool? AllowAdding { get; set; }
|
||||||
|
string Alignment { get; set; }
|
||||||
|
string Format { get; set; }
|
||||||
|
int? EditOrderNo { get; set; }
|
||||||
|
int? EditGroupOrderNo { get; set; }
|
||||||
|
string EditorType2 { get; set; }
|
||||||
|
int? ColSpan { get; set; }
|
||||||
|
string EditorOptions { get; set; }
|
||||||
|
string EditorScript { get; set; }
|
||||||
|
string ColumnCssClass { get; set; }
|
||||||
|
string ColumnCssValue { get; set; }
|
||||||
|
|
||||||
|
string ColumnFilterJson { get; set; }
|
||||||
|
string ColumnHeaderJson { get; set; }
|
||||||
|
string GroupingJson { get; set; }
|
||||||
|
string ColumnCustomizationJson { get; set; }
|
||||||
|
string TotalSummaryJson { get; set; }
|
||||||
|
string GroupSummaryJson { get; set; }
|
||||||
|
string JoinTableJson { get; set; }
|
||||||
|
string LookupJson { get; set; }
|
||||||
|
string ValidationRuleJson { get; set; }
|
||||||
|
string ColumnStylingJson { get; set; }
|
||||||
|
string PermissionJson { get; set; }
|
||||||
|
string PivotSettingsJson { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using Sozsoft.Platform.Enums;
|
using Sozsoft.Platform.Enums;
|
||||||
|
using Sozsoft.Platform.ListForms;
|
||||||
using Volo.Abp.Domain.Entities;
|
using Volo.Abp.Domain.Entities;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.Entities;
|
namespace Sozsoft.Platform.Entities;
|
||||||
|
|
||||||
public class ListForm : Entity<Guid>
|
public class ListForm : Entity<Guid>, IListFormArtifact
|
||||||
{
|
{
|
||||||
public string ListFormCode { get; set; } // Guid olan ID'den ayri olarak, bir listform'un kullanici, rol ve dil bazindaki ozellestirmelerinin ana kayit ile baglantisini kurmak icin
|
public string ListFormCode { get; set; } // Guid olan ID'den ayri olarak, bir listform'un kullanici, rol ve dil bazindaki ozellestirmelerinin ana kayit ile baglantisini kurmak icin
|
||||||
public string CultureName { get; set; } // Bu tanim hangi dil icin "en", "tr"
|
public string CultureName { get; set; } // Bu tanim hangi dil icin "en", "tr"
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
using Sozsoft.Platform.ListForms;
|
||||||
using Volo.Abp.Domain.Entities;
|
using Volo.Abp.Domain.Entities;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.Entities;
|
namespace Sozsoft.Platform.Entities;
|
||||||
|
|
||||||
public class ListFormField : Entity<Guid>
|
public class ListFormField : Entity<Guid>, IListFormFieldArtifact
|
||||||
{
|
{
|
||||||
public string ListFormCode { get; set; }// Sozsoft.LF.SATIS-323
|
public string ListFormCode { get; set; }// Sozsoft.LF.SATIS-323
|
||||||
public string UserId { get; set; } // External kullanici id (orn: ali.akman. ihtiyaca gore guid veya int de olabilir)
|
public string UserId { get; set; } // External kullanici id (orn: ali.akman. ihtiyaca gore guid veya int de olabilir)
|
||||||
|
|
|
||||||
24
claude.md
24
claude.md
|
|
@ -156,6 +156,30 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
||||||
- **`scriptRecipes.ts` (TS) ↔ `Domain.Shared/Editors/*.cs`** — biri değişirse diğeri de değişir.
|
- **`scriptRecipes.ts` (TS) ↔ `Domain.Shared/Editors/*.cs`** — biri değişirse diğeri de değişir.
|
||||||
İkisinin çıktısı **birebir aynı** olmak zorundadır; aksi hâlde seeder ile basılan script
|
İkisinin çıktısı **birebir aynı** olmak zorundadır; aksi hâlde seeder ile basılan script
|
||||||
dialogda "elle düzenlenmiş" sayılır ve kural editörü kapanır.
|
dialogda "elle düzenlenmiş" sayılır ve kural editörü kapanır.
|
||||||
|
- **Wizard seed dosyasında tekrar yok** → `Wizard` bölümü yalnızca ekrandan üretilemeyen bilgileri
|
||||||
|
taşır. `ListForm`/`Fields` içinde karşılığı olan her alan (veri kaynağı, select, anahtar alan,
|
||||||
|
kırılım/düzen/düzenleme bayrakları, görünüm option blokları, alt form, widget, iş akışı, alan
|
||||||
|
tanımları) dosyaya yazılmadan çıkarılır (`WizardSeedFileDto.ToSeedJson`) ve okurken geri
|
||||||
|
doldurulur (`FromSeedJson`). Wizard'a yeni bir alan eklerken karşılığı ListForm'da varsa listeye ekle.
|
||||||
|
Dosyada kalan wizard alanları üç başlık altında toplanır — `Menu`, `Language`, `Groups` — ve
|
||||||
|
`ListFormCode` → `WizardCode`, `MenuParentModuleId` → `ModuleId` adıyla yazılır; varsayılanı olan
|
||||||
|
alanlar (`CreateMenu: true`, boş `DataSourceConnectionString`) hiç yazılmaz. Bu yalnızca dosya biçimidir; C#
|
||||||
|
sözleşmesi düzdür, dönüşüm `WizardSeedFileDto.ToSeedJson` / `FromSeedJson` içindedir. **Wizard
|
||||||
|
seed dosyası her zaman bu iki metotla okunup yazılır**; düz `JsonSerializer` çağrısı bloklu
|
||||||
|
bölümleri sessizce düşürür.
|
||||||
|
- **Wizard ile üretilen ekran** → seed dosyası (`configs/seeds/{kapsam}/wizard/{WizardName}.json`)
|
||||||
|
wizard cevaplarının yanında ekranın `GridOptionsEditDto` (`ListForm`) ve alanların
|
||||||
|
`ColumnFormatEditDto` (`Fields`) sözleşmesini taşır. ListForm detayından yapılan her kayıt
|
||||||
|
(`ListFormsAppService`, `ListFormFieldsAppService`, `ListFormJsonRowAppService`) bu bölümleri
|
||||||
|
ve `Wizard` bölümündeki karşılıklarını geri yazar (`WizardSeedSynchronizer`). Bu üçlüden birine
|
||||||
|
yeni bir kayıt yolu eklenirse senkronizasyon çağrısı da eklenir; yoksa dosya bayatlar.
|
||||||
|
- **Wizard girdisinden ekran üretimi** → tek kaynak `ListFormWizardDto.BuildListForm/BuildFields`,
|
||||||
|
entity'ye yazma `GridOptionsEditDto.ApplyTo` / `ColumnFormatEditDto.ApplyTo`. Deploy, seeder ve
|
||||||
|
senkronizasyon aynı metotları çağırır; ikinci bir üretim kopyası yazma.
|
||||||
|
- **Katman sınırı (DDD)** → `DbMigrator` Application'a referans vermez. Seeder ile AppService'in
|
||||||
|
paylaştığı dönüşüm `Application.Contracts`'ta DTO metodudur; entity'ye yazma Domain.Shared'deki
|
||||||
|
`IListFormArtifact` / `IListFormFieldArtifact` sözleşmeleri üzerinden yapılır.
|
||||||
|
Ayrıntı: `dotnet.instructions.md` §5.
|
||||||
- **Yeni bir yetki** → `PlatformConsts.AppCodes` + `PermissionsData.json` + (UI'da kullanılacaksa)
|
- **Yeni bir yetki** → `PlatformConsts.AppCodes` + `PermissionsData.json` + (UI'da kullanılacaksa)
|
||||||
`permission.constant.ts` + `LanguagesData.json`.
|
`permission.constant.ts` + `LanguagesData.json`.
|
||||||
- **Yeni bir dil anahtarı** → `LanguagesData.json`; eklemeden önce anahtarın zaten var olup
|
- **Yeni bir dil anahtarı** → `LanguagesData.json`; eklemeden önce anahtarın zaten var olup
|
||||||
|
|
|
||||||
36
configs/seeds/host/crud/Sal_D_Appoval.json
Normal file
36
configs/seeds/host/crud/Sal_D_Appoval.json
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"EntityName": "Sal_D_Appoval",
|
||||||
|
"GeneratedAt": "2026-09-02T08:17:19.9301653Z",
|
||||||
|
"Endpoints": [
|
||||||
|
{
|
||||||
|
"Method": "POST",
|
||||||
|
"Path": "/api/app/crudendpoint/Sal_D_Appoval",
|
||||||
|
"OperationType": "Create",
|
||||||
|
"IsActive": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Method": "DELETE",
|
||||||
|
"Path": "/api/app/crudendpoint/Sal_D_Appoval/{id}",
|
||||||
|
"OperationType": "Delete",
|
||||||
|
"IsActive": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Method": "GET",
|
||||||
|
"Path": "/api/app/crudendpoint/Sal_D_Appoval/{id}",
|
||||||
|
"OperationType": "GetById",
|
||||||
|
"IsActive": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Method": "GET",
|
||||||
|
"Path": "/api/app/crudendpoint/Sal_D_Appoval",
|
||||||
|
"OperationType": "GetList",
|
||||||
|
"IsActive": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Method": "PUT",
|
||||||
|
"Path": "/api/app/crudendpoint/Sal_D_Appoval/{id}",
|
||||||
|
"OperationType": "Update",
|
||||||
|
"IsActive": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
17
configs/seeds/host/sql/object/Sal_D_Appoval.sql
Normal file
17
configs/seeds/host/sql/object/Sal_D_Appoval.sql
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
IF OBJECT_ID(N'[dbo].[Sal_D_Appoval]', 'U') IS NULL
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE [dbo].[Sal_D_Appoval]
|
||||||
|
(
|
||||||
|
[Id] uniqueidentifier NOT NULL,
|
||||||
|
[UserName] nvarchar(512) NULL,
|
||||||
|
[Status] nvarchar(100) NULL,
|
||||||
|
[Date] datetime NULL,
|
||||||
|
[Description] nvarchar(400) NULL,
|
||||||
|
[Text] nvarchar(100) NULL,
|
||||||
|
CONSTRAINT [PK_Sal_D_Appoval] PRIMARY KEY NONCLUSTERED
|
||||||
|
(
|
||||||
|
[Id] ASC
|
||||||
|
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
|
||||||
|
) ON [PRIMARY]
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
@ -1,240 +0,0 @@
|
||||||
{
|
|
||||||
"Wizard": {
|
|
||||||
"ComponentKind": 0,
|
|
||||||
"WizardName": "TaskTimesheets",
|
|
||||||
"ListFormCode": "App.Prj.TaskTimesheets",
|
|
||||||
"MenuCode": "App.Prj.TaskTimesheets",
|
|
||||||
"MenuOrder": 1,
|
|
||||||
"CreateMenu": false,
|
|
||||||
"MenuUrl": "/admin/list/App.Prj.TaskTimesheets",
|
|
||||||
"CustomComponentName": "",
|
|
||||||
"IsTenant": true,
|
|
||||||
"IsBranch": false,
|
|
||||||
"IsOrganizationUnit": false,
|
|
||||||
"AllowAdding": true,
|
|
||||||
"AllowUpdating": true,
|
|
||||||
"AllowDeleting": true,
|
|
||||||
"AllowDetail": false,
|
|
||||||
"ConfirmDelete": true,
|
|
||||||
"DefaultLayout": "grid",
|
|
||||||
"Grid": true,
|
|
||||||
"Card": false,
|
|
||||||
"Pivot": false,
|
|
||||||
"Chart": false,
|
|
||||||
"Tree": false,
|
|
||||||
"Gantt": false,
|
|
||||||
"Scheduler": false,
|
|
||||||
"Todo": false,
|
|
||||||
"LanguageTextMenuEn": "Timesheets",
|
|
||||||
"LanguageTextMenuTr": "Çalışma Çizelgeleri",
|
|
||||||
"LanguageTextTitleEn": "Timesheets",
|
|
||||||
"LanguageTextTitleTr": "Çalışma Çizelgeleri",
|
|
||||||
"LanguageTextDescEn": "Task timesheets",
|
|
||||||
"LanguageTextDescTr": "Görev çalışma çizelgeleri",
|
|
||||||
"LanguageTextMenuParentEn": "Project",
|
|
||||||
"LanguageTextMenuParentTr": "Proje",
|
|
||||||
"PermissionGroupName": "App.Prj",
|
|
||||||
"PermissionGroupDisplayNameEn": "Project",
|
|
||||||
"PermissionGroupDisplayNameTr": "Proje",
|
|
||||||
"MenuParentCode": "App.Prj",
|
|
||||||
"MenuParentModuleId": "Prj",
|
|
||||||
"MenuParentIcon": "FcTimeline",
|
|
||||||
"MenuIcon": "FcTodoList",
|
|
||||||
"DataSourceCode": "Default",
|
|
||||||
"DataSourceConnectionString": "",
|
|
||||||
"SelectCommandType": 1,
|
|
||||||
"SelectCommand": "Prj_T_Timesheet",
|
|
||||||
"KeyFieldName": "Id",
|
|
||||||
"KeyFieldDbSourceType": 9,
|
|
||||||
"TreeOptionDto": {
|
|
||||||
"KeyExpr": "",
|
|
||||||
"ParentIdExpr": "",
|
|
||||||
"HasItemsExpr": "",
|
|
||||||
"RootValue": "",
|
|
||||||
"ExpandedRowKeys": [],
|
|
||||||
"AutoExpandAll": false,
|
|
||||||
"RecursiveSelection": false,
|
|
||||||
"TitleExpr": null,
|
|
||||||
"StartExpr": null,
|
|
||||||
"EndExpr": null,
|
|
||||||
"ProgressExpr": null
|
|
||||||
},
|
|
||||||
"GanttOptionDto": {
|
|
||||||
"KeyExpr": "",
|
|
||||||
"ParentIdExpr": "",
|
|
||||||
"HasItemsExpr": "",
|
|
||||||
"RootValue": "",
|
|
||||||
"ExpandedRowKeys": [],
|
|
||||||
"AutoExpandAll": false,
|
|
||||||
"RecursiveSelection": false,
|
|
||||||
"TitleExpr": "",
|
|
||||||
"ScaleType": "weeks",
|
|
||||||
"StartExpr": "",
|
|
||||||
"EndExpr": "",
|
|
||||||
"ProgressExpr": "",
|
|
||||||
"AllowEditing": false,
|
|
||||||
"AllowTaskAdding": false,
|
|
||||||
"AllowTaskUpdating": false,
|
|
||||||
"AllowTaskResourceUpdating": false,
|
|
||||||
"AllowTaskDeleting": false,
|
|
||||||
"AllowDependencyAdding": false,
|
|
||||||
"AllowDependencyDeleting": false,
|
|
||||||
"AllowResourceAdding": false,
|
|
||||||
"AllowResourceUpdating": false,
|
|
||||||
"AllowResourceDeleting": false
|
|
||||||
},
|
|
||||||
"SchedulerOptionDto": {
|
|
||||||
"TextExpr": "",
|
|
||||||
"UserNameExpr": null,
|
|
||||||
"DescriptionExpr": null,
|
|
||||||
"StartDateExpr": "",
|
|
||||||
"EndDateExpr": "",
|
|
||||||
"AllDayExpr": "",
|
|
||||||
"RecurrenceRuleExpr": "",
|
|
||||||
"RecurrenceExceptionExpr": "",
|
|
||||||
"StartDayHour": 8,
|
|
||||||
"EndDayHour": 20,
|
|
||||||
"DefaultView": "week",
|
|
||||||
"ShowAllDayPanel": true,
|
|
||||||
"CellDuration": 30,
|
|
||||||
"FirstDayOfWeek": 1,
|
|
||||||
"CrossScrollingEnabled": false,
|
|
||||||
"AllowResizing": false,
|
|
||||||
"AllowDragging": false,
|
|
||||||
"AllowDeleting": false,
|
|
||||||
"AllowEditing": false,
|
|
||||||
"AllowAdding": false
|
|
||||||
},
|
|
||||||
"TodoOptionDto": {
|
|
||||||
"TitleExpr": "",
|
|
||||||
"StatusExpr": "",
|
|
||||||
"DescriptionExpr": "",
|
|
||||||
"DueDateExpr": "",
|
|
||||||
"TagExpr": "",
|
|
||||||
"AssigneeExpr": "",
|
|
||||||
"PriorityExpr": "",
|
|
||||||
"CompletedExpr": "",
|
|
||||||
"OrderExpr": "",
|
|
||||||
"StatusOrder": "",
|
|
||||||
"AllowDragging": true
|
|
||||||
},
|
|
||||||
"WorkflowDto": {
|
|
||||||
"ApprovalUserFieldName": "",
|
|
||||||
"ApprovalDateFieldName": "",
|
|
||||||
"ApprovalStatusFieldName": "",
|
|
||||||
"ApprovalDescriptionFieldName": "",
|
|
||||||
"ApprovalIsFilterUserName": false,
|
|
||||||
"ApprovalIsResetWorkflow": false,
|
|
||||||
"Criteria": []
|
|
||||||
},
|
|
||||||
"Groups": [
|
|
||||||
{
|
|
||||||
"Caption": "Genel",
|
|
||||||
"ColCount": 2,
|
|
||||||
"Items": [
|
|
||||||
{
|
|
||||||
"FieldName": "Id",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Id",
|
|
||||||
"TurkishCaption": "Kimlik",
|
|
||||||
"EnglishCaption": "Id",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 9,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": false,
|
|
||||||
"ColSpan": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "TaskId",
|
|
||||||
"CaptionName": "App.Listform.ListformField.TaskId",
|
|
||||||
"TurkishCaption": "Görev",
|
|
||||||
"EnglishCaption": "Task",
|
|
||||||
"EditorType": "dxSelectBox",
|
|
||||||
"DbSourceType": 9,
|
|
||||||
"IsRequired": true,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"LookupDataSourceType": 2,
|
|
||||||
"LookupQuery": "SELECT Id AS [Key], Name AS [Name] FROM dbo.Prj_T_Task WHERE IsDeleted = 0 ORDER BY Name",
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "WorkDate",
|
|
||||||
"CaptionName": "App.Listform.ListformField.WorkDate",
|
|
||||||
"TurkishCaption": "Tarih",
|
|
||||||
"EnglishCaption": "Date",
|
|
||||||
"EditorType": "dxDateBox",
|
|
||||||
"DbSourceType": 6,
|
|
||||||
"IsRequired": true,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"type\": \"date\", \"displayFormat\": \"dd.MM.yyyy\", \"useMaskBehavior\": true}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "UserId",
|
|
||||||
"CaptionName": "App.Listform.ListformField.UserId",
|
|
||||||
"TurkishCaption": "Kullanıcı",
|
|
||||||
"EnglishCaption": "User",
|
|
||||||
"EditorType": "dxSelectBox",
|
|
||||||
"DbSourceType": 9,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"LookupDataSourceType": 2,
|
|
||||||
"LookupQuery": "SELECT Id AS [Key], UserName AS [Name] FROM dbo.AbpUsers WHERE IsDeleted = 0 ORDER BY UserName",
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "Hours",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Hours",
|
|
||||||
"TurkishCaption": "Süre (saat)",
|
|
||||||
"EnglishCaption": "Hours",
|
|
||||||
"EditorType": "dxNumberBox",
|
|
||||||
"DbSourceType": 7,
|
|
||||||
"IsRequired": true,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"format\": {\"type\": \"fixedPoint\", \"precision\": 2}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "Description",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Description",
|
|
||||||
"TurkishCaption": "Açıklama",
|
|
||||||
"EnglishCaption": "Description",
|
|
||||||
"EditorType": "dxTextArea",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 2,
|
|
||||||
"EditorOptions": "{\"autoResizeEnabled\": true, \"maxLength\": 500}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"SubForms": [],
|
|
||||||
"Widgets": []
|
|
||||||
},
|
|
||||||
"IsDeletedField": true,
|
|
||||||
"IsCreatedField": true,
|
|
||||||
"InsertedRecords": {
|
|
||||||
"LanguageKeys": [
|
|
||||||
"App.Prj.TaskTimesheets",
|
|
||||||
"App.Prj"
|
|
||||||
],
|
|
||||||
"PermissionGroupNames": [
|
|
||||||
"App.Prj"
|
|
||||||
],
|
|
||||||
"PermissionNames": [
|
|
||||||
"App.Prj.TaskTimesheets",
|
|
||||||
"App.Prj.TaskTimesheets.Create",
|
|
||||||
"App.Prj.TaskTimesheets.Update",
|
|
||||||
"App.Prj.TaskTimesheets.Delete",
|
|
||||||
"App.Prj.TaskTimesheets.Export",
|
|
||||||
"App.Prj.TaskTimesheets.Import",
|
|
||||||
"App.Prj.TaskTimesheets.Note"
|
|
||||||
],
|
|
||||||
"MenuCodes": [],
|
|
||||||
"DataSourceCodes": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,265 +0,0 @@
|
||||||
{
|
|
||||||
"Wizard": {
|
|
||||||
"ComponentKind": 0,
|
|
||||||
"WizardName": "TaskSubTasks",
|
|
||||||
"ListFormCode": "App.Prj.TaskSubTasks",
|
|
||||||
"MenuCode": "App.Prj.TaskSubTasks",
|
|
||||||
"MenuOrder": 1,
|
|
||||||
"CreateMenu": false,
|
|
||||||
"MenuUrl": "/admin/list/App.Prj.TaskSubTasks",
|
|
||||||
"CustomComponentName": "",
|
|
||||||
"IsTenant": true,
|
|
||||||
"IsBranch": false,
|
|
||||||
"IsOrganizationUnit": false,
|
|
||||||
"AllowAdding": true,
|
|
||||||
"AllowUpdating": true,
|
|
||||||
"AllowDeleting": true,
|
|
||||||
"AllowDetail": false,
|
|
||||||
"ConfirmDelete": true,
|
|
||||||
"DefaultLayout": "grid",
|
|
||||||
"Grid": true,
|
|
||||||
"Card": false,
|
|
||||||
"Pivot": false,
|
|
||||||
"Chart": false,
|
|
||||||
"Tree": false,
|
|
||||||
"Gantt": false,
|
|
||||||
"Scheduler": false,
|
|
||||||
"Todo": false,
|
|
||||||
"LanguageTextMenuEn": "Sub Tasks",
|
|
||||||
"LanguageTextMenuTr": "Alt Görevler",
|
|
||||||
"LanguageTextTitleEn": "Sub Tasks",
|
|
||||||
"LanguageTextTitleTr": "Alt Görevler",
|
|
||||||
"LanguageTextDescEn": "Sub task list",
|
|
||||||
"LanguageTextDescTr": "Alt görev listesi",
|
|
||||||
"LanguageTextMenuParentEn": "Project",
|
|
||||||
"LanguageTextMenuParentTr": "Proje",
|
|
||||||
"PermissionGroupName": "App.Prj",
|
|
||||||
"PermissionGroupDisplayNameEn": "Project",
|
|
||||||
"PermissionGroupDisplayNameTr": "Proje",
|
|
||||||
"MenuParentCode": "App.Prj",
|
|
||||||
"MenuParentModuleId": "Prj",
|
|
||||||
"MenuParentIcon": "FcTimeline",
|
|
||||||
"MenuIcon": "FcTodoList",
|
|
||||||
"DataSourceCode": "Default",
|
|
||||||
"DataSourceConnectionString": "",
|
|
||||||
"SelectCommandType": 1,
|
|
||||||
"SelectCommand": "Prj_T_Task",
|
|
||||||
"KeyFieldName": "Id",
|
|
||||||
"KeyFieldDbSourceType": 9,
|
|
||||||
"TreeOptionDto": {
|
|
||||||
"KeyExpr": "",
|
|
||||||
"ParentIdExpr": "",
|
|
||||||
"HasItemsExpr": "",
|
|
||||||
"RootValue": "",
|
|
||||||
"ExpandedRowKeys": [],
|
|
||||||
"AutoExpandAll": false,
|
|
||||||
"RecursiveSelection": false,
|
|
||||||
"TitleExpr": null,
|
|
||||||
"StartExpr": null,
|
|
||||||
"EndExpr": null,
|
|
||||||
"ProgressExpr": null
|
|
||||||
},
|
|
||||||
"GanttOptionDto": {
|
|
||||||
"KeyExpr": "",
|
|
||||||
"ParentIdExpr": "",
|
|
||||||
"HasItemsExpr": "",
|
|
||||||
"RootValue": "",
|
|
||||||
"ExpandedRowKeys": [],
|
|
||||||
"AutoExpandAll": false,
|
|
||||||
"RecursiveSelection": false,
|
|
||||||
"TitleExpr": "",
|
|
||||||
"ScaleType": "weeks",
|
|
||||||
"StartExpr": "",
|
|
||||||
"EndExpr": "",
|
|
||||||
"ProgressExpr": "",
|
|
||||||
"AllowEditing": false,
|
|
||||||
"AllowTaskAdding": false,
|
|
||||||
"AllowTaskUpdating": false,
|
|
||||||
"AllowTaskResourceUpdating": false,
|
|
||||||
"AllowTaskDeleting": false,
|
|
||||||
"AllowDependencyAdding": false,
|
|
||||||
"AllowDependencyDeleting": false,
|
|
||||||
"AllowResourceAdding": false,
|
|
||||||
"AllowResourceUpdating": false,
|
|
||||||
"AllowResourceDeleting": false
|
|
||||||
},
|
|
||||||
"SchedulerOptionDto": {
|
|
||||||
"TextExpr": "",
|
|
||||||
"UserNameExpr": null,
|
|
||||||
"DescriptionExpr": null,
|
|
||||||
"StartDateExpr": "",
|
|
||||||
"EndDateExpr": "",
|
|
||||||
"AllDayExpr": "",
|
|
||||||
"RecurrenceRuleExpr": "",
|
|
||||||
"RecurrenceExceptionExpr": "",
|
|
||||||
"StartDayHour": 8,
|
|
||||||
"EndDayHour": 20,
|
|
||||||
"DefaultView": "week",
|
|
||||||
"ShowAllDayPanel": true,
|
|
||||||
"CellDuration": 30,
|
|
||||||
"FirstDayOfWeek": 1,
|
|
||||||
"CrossScrollingEnabled": false,
|
|
||||||
"AllowResizing": false,
|
|
||||||
"AllowDragging": false,
|
|
||||||
"AllowDeleting": false,
|
|
||||||
"AllowEditing": false,
|
|
||||||
"AllowAdding": false
|
|
||||||
},
|
|
||||||
"TodoOptionDto": {
|
|
||||||
"TitleExpr": "",
|
|
||||||
"StatusExpr": "",
|
|
||||||
"DescriptionExpr": "",
|
|
||||||
"DueDateExpr": "",
|
|
||||||
"TagExpr": "",
|
|
||||||
"AssigneeExpr": "",
|
|
||||||
"PriorityExpr": "",
|
|
||||||
"CompletedExpr": "",
|
|
||||||
"OrderExpr": "",
|
|
||||||
"StatusOrder": "",
|
|
||||||
"AllowDragging": true
|
|
||||||
},
|
|
||||||
"WorkflowDto": {
|
|
||||||
"ApprovalUserFieldName": "",
|
|
||||||
"ApprovalDateFieldName": "",
|
|
||||||
"ApprovalStatusFieldName": "",
|
|
||||||
"ApprovalDescriptionFieldName": "",
|
|
||||||
"ApprovalIsFilterUserName": false,
|
|
||||||
"ApprovalIsResetWorkflow": false,
|
|
||||||
"Criteria": []
|
|
||||||
},
|
|
||||||
"Groups": [
|
|
||||||
{
|
|
||||||
"Caption": "Genel",
|
|
||||||
"ColCount": 2,
|
|
||||||
"Items": [
|
|
||||||
{
|
|
||||||
"FieldName": "Id",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Id",
|
|
||||||
"TurkishCaption": "Kimlik",
|
|
||||||
"EnglishCaption": "Id",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 9,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": false,
|
|
||||||
"ColSpan": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "Name",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Name",
|
|
||||||
"TurkishCaption": "Görev Adı",
|
|
||||||
"EnglishCaption": "Task Name",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": true,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 2,
|
|
||||||
"EditorOptions": "{\"maxLength\": 300, \"showClearButton\": true}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "ParentTaskId",
|
|
||||||
"CaptionName": "App.Listform.ListformField.ParentTaskId",
|
|
||||||
"TurkishCaption": "Üst Görev",
|
|
||||||
"EnglishCaption": "Parent Task",
|
|
||||||
"EditorType": "dxSelectBox",
|
|
||||||
"DbSourceType": 9,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"LookupDataSourceType": 2,
|
|
||||||
"LookupQuery": "SELECT Id AS [Key], Name AS [Name] FROM dbo.Prj_T_Task WHERE IsDeleted = 0 ORDER BY Name",
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "Status",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Status",
|
|
||||||
"TurkishCaption": "Durum",
|
|
||||||
"EnglishCaption": "Status",
|
|
||||||
"EditorType": "dxSelectBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": true,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"LookupDataSourceType": 1,
|
|
||||||
"LookupQuery": "[{\"key\": \"Taslak\", \"name\": \"Taslak\"}, {\"key\": \"Devam Ediyor\", \"name\": \"Devam Ediyor\"}, {\"key\": \"Tamamlandı\", \"name\": \"Tamamlandı\"}, {\"key\": \"İptal\", \"name\": \"İptal\"}]",
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "AssigneeIds",
|
|
||||||
"CaptionName": "App.Listform.ListformField.AssigneeIds",
|
|
||||||
"TurkishCaption": "Atananlar",
|
|
||||||
"EnglishCaption": "Assignees",
|
|
||||||
"EditorType": "dxTagBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"showSelectionControls\": true, \"maxDisplayedTags\": 3, \"searchEnabled\": true, \"multiline\": true}",
|
|
||||||
"LookupDataSourceType": 2,
|
|
||||||
"LookupQuery": "SELECT Id AS [Key], UserName AS [Name] FROM dbo.AbpUsers WHERE IsDeleted = 0 ORDER BY UserName",
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "ProgressPercent",
|
|
||||||
"CaptionName": "App.Listform.ListformField.ProgressPercent",
|
|
||||||
"TurkishCaption": "İlerleme (%)",
|
|
||||||
"EnglishCaption": "Progress (%)",
|
|
||||||
"EditorType": "dxNumberBox",
|
|
||||||
"DbSourceType": 7,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"format\": {\"type\": \"fixedPoint\", \"precision\": 2}, \"min\": 0, \"max\": 100}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "PlannedStartDate",
|
|
||||||
"CaptionName": "App.Listform.ListformField.PlannedStartDate",
|
|
||||||
"TurkishCaption": "Planlanan Başlangıç",
|
|
||||||
"EnglishCaption": "Planned Start",
|
|
||||||
"EditorType": "dxDateBox",
|
|
||||||
"DbSourceType": 6,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"type\": \"datetime\", \"displayFormat\": \"dd.MM.yyyy HH:mm\", \"useMaskBehavior\": true}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "PlannedEndDate",
|
|
||||||
"CaptionName": "App.Listform.ListformField.PlannedEndDate",
|
|
||||||
"TurkishCaption": "Planlanan Bitiş",
|
|
||||||
"EnglishCaption": "Planned End",
|
|
||||||
"EditorType": "dxDateBox",
|
|
||||||
"DbSourceType": 6,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"type\": \"datetime\", \"displayFormat\": \"dd.MM.yyyy HH:mm\", \"useMaskBehavior\": true}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"SubForms": [],
|
|
||||||
"Widgets": []
|
|
||||||
},
|
|
||||||
"IsDeletedField": true,
|
|
||||||
"IsCreatedField": true,
|
|
||||||
"InsertedRecords": {
|
|
||||||
"LanguageKeys": [
|
|
||||||
"App.Prj.TaskSubTasks"
|
|
||||||
],
|
|
||||||
"PermissionGroupNames": [],
|
|
||||||
"PermissionNames": [
|
|
||||||
"App.Prj.TaskSubTasks",
|
|
||||||
"App.Prj.TaskSubTasks.Create",
|
|
||||||
"App.Prj.TaskSubTasks.Update",
|
|
||||||
"App.Prj.TaskSubTasks.Delete",
|
|
||||||
"App.Prj.TaskSubTasks.Export",
|
|
||||||
"App.Prj.TaskSubTasks.Import",
|
|
||||||
"App.Prj.TaskSubTasks.Note"
|
|
||||||
],
|
|
||||||
"MenuCodes": [],
|
|
||||||
"DataSourceCodes": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,213 +0,0 @@
|
||||||
{
|
|
||||||
"Wizard": {
|
|
||||||
"ComponentKind": 0,
|
|
||||||
"WizardName": "TaskDependencies",
|
|
||||||
"ListFormCode": "App.Prj.TaskDependencies",
|
|
||||||
"MenuCode": "App.Prj.TaskDependencies",
|
|
||||||
"MenuOrder": 1,
|
|
||||||
"CreateMenu": false,
|
|
||||||
"MenuUrl": "/admin/list/App.Prj.TaskDependencies",
|
|
||||||
"CustomComponentName": "",
|
|
||||||
"IsTenant": true,
|
|
||||||
"IsBranch": false,
|
|
||||||
"IsOrganizationUnit": false,
|
|
||||||
"AllowAdding": true,
|
|
||||||
"AllowUpdating": true,
|
|
||||||
"AllowDeleting": true,
|
|
||||||
"AllowDetail": false,
|
|
||||||
"ConfirmDelete": true,
|
|
||||||
"DefaultLayout": "grid",
|
|
||||||
"Grid": true,
|
|
||||||
"Card": false,
|
|
||||||
"Pivot": false,
|
|
||||||
"Chart": false,
|
|
||||||
"Tree": false,
|
|
||||||
"Gantt": false,
|
|
||||||
"Scheduler": false,
|
|
||||||
"Todo": false,
|
|
||||||
"LanguageTextMenuEn": "Blocking Tasks",
|
|
||||||
"LanguageTextMenuTr": "Beklenen Görevler",
|
|
||||||
"LanguageTextTitleEn": "Blocking Tasks",
|
|
||||||
"LanguageTextTitleTr": "Beklenen Görevler",
|
|
||||||
"LanguageTextDescEn": "Tasks this task waits for",
|
|
||||||
"LanguageTextDescTr": "Görevin beklediği görevler",
|
|
||||||
"LanguageTextMenuParentEn": "Project",
|
|
||||||
"LanguageTextMenuParentTr": "Proje",
|
|
||||||
"PermissionGroupName": "App.Prj",
|
|
||||||
"PermissionGroupDisplayNameEn": "Project",
|
|
||||||
"PermissionGroupDisplayNameTr": "Proje",
|
|
||||||
"MenuParentCode": "App.Prj",
|
|
||||||
"MenuParentModuleId": "Prj",
|
|
||||||
"MenuParentIcon": "FcTimeline",
|
|
||||||
"MenuIcon": "FcTodoList",
|
|
||||||
"DataSourceCode": "Default",
|
|
||||||
"DataSourceConnectionString": "",
|
|
||||||
"SelectCommandType": 1,
|
|
||||||
"SelectCommand": "Prj_T_TaskDependency",
|
|
||||||
"KeyFieldName": "Id",
|
|
||||||
"KeyFieldDbSourceType": 9,
|
|
||||||
"TreeOptionDto": {
|
|
||||||
"KeyExpr": "",
|
|
||||||
"ParentIdExpr": "",
|
|
||||||
"HasItemsExpr": "",
|
|
||||||
"RootValue": "",
|
|
||||||
"ExpandedRowKeys": [],
|
|
||||||
"AutoExpandAll": false,
|
|
||||||
"RecursiveSelection": false,
|
|
||||||
"TitleExpr": null,
|
|
||||||
"StartExpr": null,
|
|
||||||
"EndExpr": null,
|
|
||||||
"ProgressExpr": null
|
|
||||||
},
|
|
||||||
"GanttOptionDto": {
|
|
||||||
"KeyExpr": "",
|
|
||||||
"ParentIdExpr": "",
|
|
||||||
"HasItemsExpr": "",
|
|
||||||
"RootValue": "",
|
|
||||||
"ExpandedRowKeys": [],
|
|
||||||
"AutoExpandAll": false,
|
|
||||||
"RecursiveSelection": false,
|
|
||||||
"TitleExpr": "",
|
|
||||||
"ScaleType": "weeks",
|
|
||||||
"StartExpr": "",
|
|
||||||
"EndExpr": "",
|
|
||||||
"ProgressExpr": "",
|
|
||||||
"AllowEditing": false,
|
|
||||||
"AllowTaskAdding": false,
|
|
||||||
"AllowTaskUpdating": false,
|
|
||||||
"AllowTaskResourceUpdating": false,
|
|
||||||
"AllowTaskDeleting": false,
|
|
||||||
"AllowDependencyAdding": false,
|
|
||||||
"AllowDependencyDeleting": false,
|
|
||||||
"AllowResourceAdding": false,
|
|
||||||
"AllowResourceUpdating": false,
|
|
||||||
"AllowResourceDeleting": false
|
|
||||||
},
|
|
||||||
"SchedulerOptionDto": {
|
|
||||||
"TextExpr": "",
|
|
||||||
"UserNameExpr": null,
|
|
||||||
"DescriptionExpr": null,
|
|
||||||
"StartDateExpr": "",
|
|
||||||
"EndDateExpr": "",
|
|
||||||
"AllDayExpr": "",
|
|
||||||
"RecurrenceRuleExpr": "",
|
|
||||||
"RecurrenceExceptionExpr": "",
|
|
||||||
"StartDayHour": 8,
|
|
||||||
"EndDayHour": 20,
|
|
||||||
"DefaultView": "week",
|
|
||||||
"ShowAllDayPanel": true,
|
|
||||||
"CellDuration": 30,
|
|
||||||
"FirstDayOfWeek": 1,
|
|
||||||
"CrossScrollingEnabled": false,
|
|
||||||
"AllowResizing": false,
|
|
||||||
"AllowDragging": false,
|
|
||||||
"AllowDeleting": false,
|
|
||||||
"AllowEditing": false,
|
|
||||||
"AllowAdding": false
|
|
||||||
},
|
|
||||||
"TodoOptionDto": {
|
|
||||||
"TitleExpr": "",
|
|
||||||
"StatusExpr": "",
|
|
||||||
"DescriptionExpr": "",
|
|
||||||
"DueDateExpr": "",
|
|
||||||
"TagExpr": "",
|
|
||||||
"AssigneeExpr": "",
|
|
||||||
"PriorityExpr": "",
|
|
||||||
"CompletedExpr": "",
|
|
||||||
"OrderExpr": "",
|
|
||||||
"StatusOrder": "",
|
|
||||||
"AllowDragging": true
|
|
||||||
},
|
|
||||||
"WorkflowDto": {
|
|
||||||
"ApprovalUserFieldName": "",
|
|
||||||
"ApprovalDateFieldName": "",
|
|
||||||
"ApprovalStatusFieldName": "",
|
|
||||||
"ApprovalDescriptionFieldName": "",
|
|
||||||
"ApprovalIsFilterUserName": false,
|
|
||||||
"ApprovalIsResetWorkflow": false,
|
|
||||||
"Criteria": []
|
|
||||||
},
|
|
||||||
"Groups": [
|
|
||||||
{
|
|
||||||
"Caption": "Genel",
|
|
||||||
"ColCount": 2,
|
|
||||||
"Items": [
|
|
||||||
{
|
|
||||||
"FieldName": "Id",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Id",
|
|
||||||
"TurkishCaption": "Kimlik",
|
|
||||||
"EnglishCaption": "Id",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 9,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": false,
|
|
||||||
"ColSpan": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "TaskId",
|
|
||||||
"CaptionName": "App.Listform.ListformField.TaskId",
|
|
||||||
"TurkishCaption": "Görev",
|
|
||||||
"EnglishCaption": "Task",
|
|
||||||
"EditorType": "dxSelectBox",
|
|
||||||
"DbSourceType": 9,
|
|
||||||
"IsRequired": true,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"LookupDataSourceType": 2,
|
|
||||||
"LookupQuery": "SELECT Id AS [Key], Name AS [Name] FROM dbo.Prj_T_Task WHERE IsDeleted = 0 ORDER BY Name",
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "DependsOnTaskId",
|
|
||||||
"CaptionName": "App.Listform.ListformField.DependsOnTaskId",
|
|
||||||
"TurkishCaption": "Beklenen Görev",
|
|
||||||
"EnglishCaption": "Blocking Task",
|
|
||||||
"EditorType": "dxSelectBox",
|
|
||||||
"DbSourceType": 9,
|
|
||||||
"IsRequired": true,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"LookupDataSourceType": 2,
|
|
||||||
"LookupQuery": "SELECT Id AS [Key], Name AS [Name] FROM dbo.Prj_T_Task WHERE IsDeleted = 0 ORDER BY Name",
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "Note",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Note",
|
|
||||||
"TurkishCaption": "Not",
|
|
||||||
"EnglishCaption": "Note",
|
|
||||||
"EditorType": "dxTextArea",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 2,
|
|
||||||
"EditorOptions": "{\"autoResizeEnabled\": true, \"maxLength\": 300}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"SubForms": [],
|
|
||||||
"Widgets": []
|
|
||||||
},
|
|
||||||
"IsDeletedField": true,
|
|
||||||
"IsCreatedField": true,
|
|
||||||
"InsertedRecords": {
|
|
||||||
"LanguageKeys": [
|
|
||||||
"App.Prj.TaskDependencies"
|
|
||||||
],
|
|
||||||
"PermissionGroupNames": [],
|
|
||||||
"PermissionNames": [
|
|
||||||
"App.Prj.TaskDependencies",
|
|
||||||
"App.Prj.TaskDependencies.Create",
|
|
||||||
"App.Prj.TaskDependencies.Update",
|
|
||||||
"App.Prj.TaskDependencies.Delete",
|
|
||||||
"App.Prj.TaskDependencies.Export",
|
|
||||||
"App.Prj.TaskDependencies.Import",
|
|
||||||
"App.Prj.TaskDependencies.Note"
|
|
||||||
],
|
|
||||||
"MenuCodes": [],
|
|
||||||
"DataSourceCodes": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,197 +0,0 @@
|
||||||
{
|
|
||||||
"Wizard": {
|
|
||||||
"ComponentKind": 0,
|
|
||||||
"WizardName": "Tags",
|
|
||||||
"ListFormCode": "App.Prj.Tags",
|
|
||||||
"MenuCode": "App.Prj.Tags",
|
|
||||||
"MenuOrder": 2,
|
|
||||||
"CreateMenu": true,
|
|
||||||
"MenuUrl": "/admin/list/App.Prj.Tags",
|
|
||||||
"CustomComponentName": "",
|
|
||||||
"IsTenant": true,
|
|
||||||
"IsBranch": false,
|
|
||||||
"IsOrganizationUnit": false,
|
|
||||||
"AllowAdding": true,
|
|
||||||
"AllowUpdating": true,
|
|
||||||
"AllowDeleting": true,
|
|
||||||
"AllowDetail": false,
|
|
||||||
"ConfirmDelete": true,
|
|
||||||
"DefaultLayout": "grid",
|
|
||||||
"Grid": true,
|
|
||||||
"Card": false,
|
|
||||||
"Pivot": false,
|
|
||||||
"Chart": false,
|
|
||||||
"Tree": false,
|
|
||||||
"Gantt": false,
|
|
||||||
"Scheduler": false,
|
|
||||||
"Todo": false,
|
|
||||||
"LanguageTextMenuEn": "Tags",
|
|
||||||
"LanguageTextMenuTr": "Etiketler",
|
|
||||||
"LanguageTextTitleEn": "Tags",
|
|
||||||
"LanguageTextTitleTr": "Etiketler",
|
|
||||||
"LanguageTextDescEn": "Task tags",
|
|
||||||
"LanguageTextDescTr": "Görev etiketleri",
|
|
||||||
"LanguageTextMenuParentEn": "Project",
|
|
||||||
"LanguageTextMenuParentTr": "Proje",
|
|
||||||
"PermissionGroupName": "App.Prj",
|
|
||||||
"PermissionGroupDisplayNameEn": "Project",
|
|
||||||
"PermissionGroupDisplayNameTr": "Proje",
|
|
||||||
"MenuParentCode": "App.Prj",
|
|
||||||
"MenuParentModuleId": "Prj",
|
|
||||||
"MenuParentIcon": "FcTimeline",
|
|
||||||
"MenuIcon": "FcRules",
|
|
||||||
"DataSourceCode": "Default",
|
|
||||||
"DataSourceConnectionString": "",
|
|
||||||
"SelectCommandType": 1,
|
|
||||||
"SelectCommand": "Prj_T_Tag",
|
|
||||||
"KeyFieldName": "Id",
|
|
||||||
"KeyFieldDbSourceType": 9,
|
|
||||||
"TreeOptionDto": {
|
|
||||||
"KeyExpr": "",
|
|
||||||
"ParentIdExpr": "",
|
|
||||||
"HasItemsExpr": "",
|
|
||||||
"RootValue": "",
|
|
||||||
"ExpandedRowKeys": [],
|
|
||||||
"AutoExpandAll": false,
|
|
||||||
"RecursiveSelection": false,
|
|
||||||
"TitleExpr": null,
|
|
||||||
"StartExpr": null,
|
|
||||||
"EndExpr": null,
|
|
||||||
"ProgressExpr": null
|
|
||||||
},
|
|
||||||
"GanttOptionDto": {
|
|
||||||
"KeyExpr": "",
|
|
||||||
"ParentIdExpr": "",
|
|
||||||
"HasItemsExpr": "",
|
|
||||||
"RootValue": "",
|
|
||||||
"ExpandedRowKeys": [],
|
|
||||||
"AutoExpandAll": false,
|
|
||||||
"RecursiveSelection": false,
|
|
||||||
"TitleExpr": "",
|
|
||||||
"ScaleType": "weeks",
|
|
||||||
"StartExpr": "",
|
|
||||||
"EndExpr": "",
|
|
||||||
"ProgressExpr": "",
|
|
||||||
"AllowEditing": false,
|
|
||||||
"AllowTaskAdding": false,
|
|
||||||
"AllowTaskUpdating": false,
|
|
||||||
"AllowTaskResourceUpdating": false,
|
|
||||||
"AllowTaskDeleting": false,
|
|
||||||
"AllowDependencyAdding": false,
|
|
||||||
"AllowDependencyDeleting": false,
|
|
||||||
"AllowResourceAdding": false,
|
|
||||||
"AllowResourceUpdating": false,
|
|
||||||
"AllowResourceDeleting": false
|
|
||||||
},
|
|
||||||
"SchedulerOptionDto": {
|
|
||||||
"TextExpr": "",
|
|
||||||
"UserNameExpr": null,
|
|
||||||
"DescriptionExpr": null,
|
|
||||||
"StartDateExpr": "",
|
|
||||||
"EndDateExpr": "",
|
|
||||||
"AllDayExpr": "",
|
|
||||||
"RecurrenceRuleExpr": "",
|
|
||||||
"RecurrenceExceptionExpr": "",
|
|
||||||
"StartDayHour": 8,
|
|
||||||
"EndDayHour": 20,
|
|
||||||
"DefaultView": "week",
|
|
||||||
"ShowAllDayPanel": true,
|
|
||||||
"CellDuration": 30,
|
|
||||||
"FirstDayOfWeek": 1,
|
|
||||||
"CrossScrollingEnabled": false,
|
|
||||||
"AllowResizing": false,
|
|
||||||
"AllowDragging": false,
|
|
||||||
"AllowDeleting": false,
|
|
||||||
"AllowEditing": false,
|
|
||||||
"AllowAdding": false
|
|
||||||
},
|
|
||||||
"TodoOptionDto": {
|
|
||||||
"TitleExpr": "",
|
|
||||||
"StatusExpr": "",
|
|
||||||
"DescriptionExpr": "",
|
|
||||||
"DueDateExpr": "",
|
|
||||||
"TagExpr": "",
|
|
||||||
"AssigneeExpr": "",
|
|
||||||
"PriorityExpr": "",
|
|
||||||
"CompletedExpr": "",
|
|
||||||
"OrderExpr": "",
|
|
||||||
"StatusOrder": "",
|
|
||||||
"AllowDragging": true
|
|
||||||
},
|
|
||||||
"WorkflowDto": {
|
|
||||||
"ApprovalUserFieldName": "",
|
|
||||||
"ApprovalDateFieldName": "",
|
|
||||||
"ApprovalStatusFieldName": "",
|
|
||||||
"ApprovalDescriptionFieldName": "",
|
|
||||||
"ApprovalIsFilterUserName": false,
|
|
||||||
"ApprovalIsResetWorkflow": false,
|
|
||||||
"Criteria": []
|
|
||||||
},
|
|
||||||
"Groups": [
|
|
||||||
{
|
|
||||||
"Caption": "Genel",
|
|
||||||
"ColCount": 2,
|
|
||||||
"Items": [
|
|
||||||
{
|
|
||||||
"FieldName": "Id",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Id",
|
|
||||||
"TurkishCaption": "Kimlik",
|
|
||||||
"EnglishCaption": "Id",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 9,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": false,
|
|
||||||
"ColSpan": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "Name",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Name",
|
|
||||||
"TurkishCaption": "Etiket Adı",
|
|
||||||
"EnglishCaption": "Tag Name",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": true,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"maxLength\": 200, \"showClearButton\": true}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "ColorCode",
|
|
||||||
"CaptionName": "App.Listform.ListformField.ColorCode",
|
|
||||||
"TurkishCaption": "Renk",
|
|
||||||
"EnglishCaption": "Color",
|
|
||||||
"EditorType": "dxColorBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"SubForms": [],
|
|
||||||
"Widgets": []
|
|
||||||
},
|
|
||||||
"IsDeletedField": true,
|
|
||||||
"IsCreatedField": true,
|
|
||||||
"InsertedRecords": {
|
|
||||||
"LanguageKeys": [
|
|
||||||
"App.Prj.Tags"
|
|
||||||
],
|
|
||||||
"PermissionGroupNames": [],
|
|
||||||
"PermissionNames": [
|
|
||||||
"App.Prj.Tags",
|
|
||||||
"App.Prj.Tags.Create",
|
|
||||||
"App.Prj.Tags.Update",
|
|
||||||
"App.Prj.Tags.Delete",
|
|
||||||
"App.Prj.Tags.Export",
|
|
||||||
"App.Prj.Tags.Import",
|
|
||||||
"App.Prj.Tags.Note"
|
|
||||||
],
|
|
||||||
"MenuCodes": [
|
|
||||||
"App.Prj",
|
|
||||||
"App.Prj.Tags"
|
|
||||||
],
|
|
||||||
"DataSourceCodes": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,208 +0,0 @@
|
||||||
{
|
|
||||||
"Wizard": {
|
|
||||||
"ComponentKind": 0,
|
|
||||||
"WizardName": "Milestones",
|
|
||||||
"ListFormCode": "App.Prj.Milestones",
|
|
||||||
"MenuCode": "App.Prj.Milestones",
|
|
||||||
"MenuOrder": 3,
|
|
||||||
"CreateMenu": true,
|
|
||||||
"MenuUrl": "/admin/list/App.Prj.Milestones",
|
|
||||||
"CustomComponentName": "",
|
|
||||||
"IsTenant": true,
|
|
||||||
"IsBranch": false,
|
|
||||||
"IsOrganizationUnit": false,
|
|
||||||
"AllowAdding": true,
|
|
||||||
"AllowUpdating": true,
|
|
||||||
"AllowDeleting": true,
|
|
||||||
"AllowDetail": false,
|
|
||||||
"ConfirmDelete": true,
|
|
||||||
"DefaultLayout": "grid",
|
|
||||||
"Grid": true,
|
|
||||||
"Card": false,
|
|
||||||
"Pivot": false,
|
|
||||||
"Chart": false,
|
|
||||||
"Tree": false,
|
|
||||||
"Gantt": false,
|
|
||||||
"Scheduler": false,
|
|
||||||
"Todo": false,
|
|
||||||
"LanguageTextMenuEn": "Milestones",
|
|
||||||
"LanguageTextMenuTr": "Dönüm Noktaları",
|
|
||||||
"LanguageTextTitleEn": "Milestones",
|
|
||||||
"LanguageTextTitleTr": "Dönüm Noktaları",
|
|
||||||
"LanguageTextDescEn": "Project milestones",
|
|
||||||
"LanguageTextDescTr": "Proje dönüm noktaları",
|
|
||||||
"LanguageTextMenuParentEn": "Project",
|
|
||||||
"LanguageTextMenuParentTr": "Proje",
|
|
||||||
"PermissionGroupName": "App.Prj",
|
|
||||||
"PermissionGroupDisplayNameEn": "Project",
|
|
||||||
"PermissionGroupDisplayNameTr": "Proje",
|
|
||||||
"MenuParentCode": "App.Prj",
|
|
||||||
"MenuParentModuleId": "Prj",
|
|
||||||
"MenuParentIcon": "FcTimeline",
|
|
||||||
"MenuIcon": "FcCalendar",
|
|
||||||
"DataSourceCode": "Default",
|
|
||||||
"DataSourceConnectionString": "",
|
|
||||||
"SelectCommandType": 1,
|
|
||||||
"SelectCommand": "Prj_T_Milestone",
|
|
||||||
"KeyFieldName": "Id",
|
|
||||||
"KeyFieldDbSourceType": 9,
|
|
||||||
"TreeOptionDto": {
|
|
||||||
"KeyExpr": "",
|
|
||||||
"ParentIdExpr": "",
|
|
||||||
"HasItemsExpr": "",
|
|
||||||
"RootValue": "",
|
|
||||||
"ExpandedRowKeys": [],
|
|
||||||
"AutoExpandAll": false,
|
|
||||||
"RecursiveSelection": false,
|
|
||||||
"TitleExpr": null,
|
|
||||||
"StartExpr": null,
|
|
||||||
"EndExpr": null,
|
|
||||||
"ProgressExpr": null
|
|
||||||
},
|
|
||||||
"GanttOptionDto": {
|
|
||||||
"KeyExpr": "",
|
|
||||||
"ParentIdExpr": "",
|
|
||||||
"HasItemsExpr": "",
|
|
||||||
"RootValue": "",
|
|
||||||
"ExpandedRowKeys": [],
|
|
||||||
"AutoExpandAll": false,
|
|
||||||
"RecursiveSelection": false,
|
|
||||||
"TitleExpr": "",
|
|
||||||
"ScaleType": "weeks",
|
|
||||||
"StartExpr": "",
|
|
||||||
"EndExpr": "",
|
|
||||||
"ProgressExpr": "",
|
|
||||||
"AllowEditing": false,
|
|
||||||
"AllowTaskAdding": false,
|
|
||||||
"AllowTaskUpdating": false,
|
|
||||||
"AllowTaskResourceUpdating": false,
|
|
||||||
"AllowTaskDeleting": false,
|
|
||||||
"AllowDependencyAdding": false,
|
|
||||||
"AllowDependencyDeleting": false,
|
|
||||||
"AllowResourceAdding": false,
|
|
||||||
"AllowResourceUpdating": false,
|
|
||||||
"AllowResourceDeleting": false
|
|
||||||
},
|
|
||||||
"SchedulerOptionDto": {
|
|
||||||
"TextExpr": "",
|
|
||||||
"UserNameExpr": null,
|
|
||||||
"DescriptionExpr": null,
|
|
||||||
"StartDateExpr": "",
|
|
||||||
"EndDateExpr": "",
|
|
||||||
"AllDayExpr": "",
|
|
||||||
"RecurrenceRuleExpr": "",
|
|
||||||
"RecurrenceExceptionExpr": "",
|
|
||||||
"StartDayHour": 8,
|
|
||||||
"EndDayHour": 20,
|
|
||||||
"DefaultView": "week",
|
|
||||||
"ShowAllDayPanel": true,
|
|
||||||
"CellDuration": 30,
|
|
||||||
"FirstDayOfWeek": 1,
|
|
||||||
"CrossScrollingEnabled": false,
|
|
||||||
"AllowResizing": false,
|
|
||||||
"AllowDragging": false,
|
|
||||||
"AllowDeleting": false,
|
|
||||||
"AllowEditing": false,
|
|
||||||
"AllowAdding": false
|
|
||||||
},
|
|
||||||
"TodoOptionDto": {
|
|
||||||
"TitleExpr": "",
|
|
||||||
"StatusExpr": "",
|
|
||||||
"DescriptionExpr": "",
|
|
||||||
"DueDateExpr": "",
|
|
||||||
"TagExpr": "",
|
|
||||||
"AssigneeExpr": "",
|
|
||||||
"PriorityExpr": "",
|
|
||||||
"CompletedExpr": "",
|
|
||||||
"OrderExpr": "",
|
|
||||||
"StatusOrder": "",
|
|
||||||
"AllowDragging": true
|
|
||||||
},
|
|
||||||
"WorkflowDto": {
|
|
||||||
"ApprovalUserFieldName": "",
|
|
||||||
"ApprovalDateFieldName": "",
|
|
||||||
"ApprovalStatusFieldName": "",
|
|
||||||
"ApprovalDescriptionFieldName": "",
|
|
||||||
"ApprovalIsFilterUserName": false,
|
|
||||||
"ApprovalIsResetWorkflow": false,
|
|
||||||
"Criteria": []
|
|
||||||
},
|
|
||||||
"Groups": [
|
|
||||||
{
|
|
||||||
"Caption": "Genel",
|
|
||||||
"ColCount": 2,
|
|
||||||
"Items": [
|
|
||||||
{
|
|
||||||
"FieldName": "Id",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Id",
|
|
||||||
"TurkishCaption": "Kimlik",
|
|
||||||
"EnglishCaption": "Id",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 9,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": false,
|
|
||||||
"ColSpan": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "Name",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Name",
|
|
||||||
"TurkishCaption": "Dönüm Noktası",
|
|
||||||
"EnglishCaption": "Milestone",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": true,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 2,
|
|
||||||
"EditorOptions": "{\"maxLength\": 300, \"showClearButton\": true}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "TargetDate",
|
|
||||||
"CaptionName": "App.Listform.ListformField.TargetDate",
|
|
||||||
"TurkishCaption": "Hedef Tarih",
|
|
||||||
"EnglishCaption": "Target Date",
|
|
||||||
"EditorType": "dxDateBox",
|
|
||||||
"DbSourceType": 6,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"type\": \"date\", \"displayFormat\": \"dd.MM.yyyy\", \"useMaskBehavior\": true}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "IsCompleted",
|
|
||||||
"CaptionName": "App.Listform.ListformField.IsCompleted",
|
|
||||||
"TurkishCaption": "Tamamlandı",
|
|
||||||
"EnglishCaption": "Completed",
|
|
||||||
"EditorType": "dxCheckBox",
|
|
||||||
"DbSourceType": 3,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"SubForms": [],
|
|
||||||
"Widgets": []
|
|
||||||
},
|
|
||||||
"IsDeletedField": true,
|
|
||||||
"IsCreatedField": true,
|
|
||||||
"InsertedRecords": {
|
|
||||||
"LanguageKeys": [
|
|
||||||
"App.Prj.Milestones"
|
|
||||||
],
|
|
||||||
"PermissionGroupNames": [],
|
|
||||||
"PermissionNames": [
|
|
||||||
"App.Prj.Milestones",
|
|
||||||
"App.Prj.Milestones.Create",
|
|
||||||
"App.Prj.Milestones.Update",
|
|
||||||
"App.Prj.Milestones.Delete",
|
|
||||||
"App.Prj.Milestones.Export",
|
|
||||||
"App.Prj.Milestones.Import",
|
|
||||||
"App.Prj.Milestones.Note"
|
|
||||||
],
|
|
||||||
"MenuCodes": [
|
|
||||||
"App.Prj.Milestones"
|
|
||||||
],
|
|
||||||
"DataSourceCodes": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,221 +0,0 @@
|
||||||
{
|
|
||||||
"Wizard": {
|
|
||||||
"ComponentKind": 0,
|
|
||||||
"WizardName": "Customers",
|
|
||||||
"ListFormCode": "App.Prj.Customers",
|
|
||||||
"MenuCode": "App.Prj.Customers",
|
|
||||||
"MenuOrder": 4,
|
|
||||||
"CreateMenu": true,
|
|
||||||
"MenuUrl": "/admin/list/App.Prj.Customers",
|
|
||||||
"CustomComponentName": "",
|
|
||||||
"IsTenant": true,
|
|
||||||
"IsBranch": false,
|
|
||||||
"IsOrganizationUnit": false,
|
|
||||||
"AllowAdding": true,
|
|
||||||
"AllowUpdating": true,
|
|
||||||
"AllowDeleting": true,
|
|
||||||
"AllowDetail": false,
|
|
||||||
"ConfirmDelete": true,
|
|
||||||
"DefaultLayout": "grid",
|
|
||||||
"Grid": true,
|
|
||||||
"Card": false,
|
|
||||||
"Pivot": false,
|
|
||||||
"Chart": false,
|
|
||||||
"Tree": false,
|
|
||||||
"Gantt": false,
|
|
||||||
"Scheduler": false,
|
|
||||||
"Todo": false,
|
|
||||||
"LanguageTextMenuEn": "Customers",
|
|
||||||
"LanguageTextMenuTr": "Müşteriler",
|
|
||||||
"LanguageTextTitleEn": "Customers",
|
|
||||||
"LanguageTextTitleTr": "Müşteriler",
|
|
||||||
"LanguageTextDescEn": "Customers linked to tasks",
|
|
||||||
"LanguageTextDescTr": "Görevlerin bağlı olduğu müşteriler",
|
|
||||||
"LanguageTextMenuParentEn": "Project",
|
|
||||||
"LanguageTextMenuParentTr": "Proje",
|
|
||||||
"PermissionGroupName": "App.Prj",
|
|
||||||
"PermissionGroupDisplayNameEn": "Project",
|
|
||||||
"PermissionGroupDisplayNameTr": "Proje",
|
|
||||||
"MenuParentCode": "App.Prj",
|
|
||||||
"MenuParentModuleId": "Prj",
|
|
||||||
"MenuParentIcon": "FcTimeline",
|
|
||||||
"MenuIcon": "FcBusinessContact",
|
|
||||||
"DataSourceCode": "Default",
|
|
||||||
"DataSourceConnectionString": "",
|
|
||||||
"SelectCommandType": 1,
|
|
||||||
"SelectCommand": "Prj_T_Customer",
|
|
||||||
"KeyFieldName": "Id",
|
|
||||||
"KeyFieldDbSourceType": 9,
|
|
||||||
"TreeOptionDto": {
|
|
||||||
"KeyExpr": "",
|
|
||||||
"ParentIdExpr": "",
|
|
||||||
"HasItemsExpr": "",
|
|
||||||
"RootValue": "",
|
|
||||||
"ExpandedRowKeys": [],
|
|
||||||
"AutoExpandAll": false,
|
|
||||||
"RecursiveSelection": false,
|
|
||||||
"TitleExpr": null,
|
|
||||||
"StartExpr": null,
|
|
||||||
"EndExpr": null,
|
|
||||||
"ProgressExpr": null
|
|
||||||
},
|
|
||||||
"GanttOptionDto": {
|
|
||||||
"KeyExpr": "",
|
|
||||||
"ParentIdExpr": "",
|
|
||||||
"HasItemsExpr": "",
|
|
||||||
"RootValue": "",
|
|
||||||
"ExpandedRowKeys": [],
|
|
||||||
"AutoExpandAll": false,
|
|
||||||
"RecursiveSelection": false,
|
|
||||||
"TitleExpr": "",
|
|
||||||
"ScaleType": "weeks",
|
|
||||||
"StartExpr": "",
|
|
||||||
"EndExpr": "",
|
|
||||||
"ProgressExpr": "",
|
|
||||||
"AllowEditing": false,
|
|
||||||
"AllowTaskAdding": false,
|
|
||||||
"AllowTaskUpdating": false,
|
|
||||||
"AllowTaskResourceUpdating": false,
|
|
||||||
"AllowTaskDeleting": false,
|
|
||||||
"AllowDependencyAdding": false,
|
|
||||||
"AllowDependencyDeleting": false,
|
|
||||||
"AllowResourceAdding": false,
|
|
||||||
"AllowResourceUpdating": false,
|
|
||||||
"AllowResourceDeleting": false
|
|
||||||
},
|
|
||||||
"SchedulerOptionDto": {
|
|
||||||
"TextExpr": "",
|
|
||||||
"UserNameExpr": null,
|
|
||||||
"DescriptionExpr": null,
|
|
||||||
"StartDateExpr": "",
|
|
||||||
"EndDateExpr": "",
|
|
||||||
"AllDayExpr": "",
|
|
||||||
"RecurrenceRuleExpr": "",
|
|
||||||
"RecurrenceExceptionExpr": "",
|
|
||||||
"StartDayHour": 8,
|
|
||||||
"EndDayHour": 20,
|
|
||||||
"DefaultView": "week",
|
|
||||||
"ShowAllDayPanel": true,
|
|
||||||
"CellDuration": 30,
|
|
||||||
"FirstDayOfWeek": 1,
|
|
||||||
"CrossScrollingEnabled": false,
|
|
||||||
"AllowResizing": false,
|
|
||||||
"AllowDragging": false,
|
|
||||||
"AllowDeleting": false,
|
|
||||||
"AllowEditing": false,
|
|
||||||
"AllowAdding": false
|
|
||||||
},
|
|
||||||
"TodoOptionDto": {
|
|
||||||
"TitleExpr": "",
|
|
||||||
"StatusExpr": "",
|
|
||||||
"DescriptionExpr": "",
|
|
||||||
"DueDateExpr": "",
|
|
||||||
"TagExpr": "",
|
|
||||||
"AssigneeExpr": "",
|
|
||||||
"PriorityExpr": "",
|
|
||||||
"CompletedExpr": "",
|
|
||||||
"OrderExpr": "",
|
|
||||||
"StatusOrder": "",
|
|
||||||
"AllowDragging": true
|
|
||||||
},
|
|
||||||
"WorkflowDto": {
|
|
||||||
"ApprovalUserFieldName": "",
|
|
||||||
"ApprovalDateFieldName": "",
|
|
||||||
"ApprovalStatusFieldName": "",
|
|
||||||
"ApprovalDescriptionFieldName": "",
|
|
||||||
"ApprovalIsFilterUserName": false,
|
|
||||||
"ApprovalIsResetWorkflow": false,
|
|
||||||
"Criteria": []
|
|
||||||
},
|
|
||||||
"Groups": [
|
|
||||||
{
|
|
||||||
"Caption": "Genel",
|
|
||||||
"ColCount": 2,
|
|
||||||
"Items": [
|
|
||||||
{
|
|
||||||
"FieldName": "Id",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Id",
|
|
||||||
"TurkishCaption": "Kimlik",
|
|
||||||
"EnglishCaption": "Id",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 9,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": false,
|
|
||||||
"ColSpan": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "Name",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Name",
|
|
||||||
"TurkishCaption": "Müşteri",
|
|
||||||
"EnglishCaption": "Customer",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": true,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 2,
|
|
||||||
"EditorOptions": "{\"maxLength\": 300, \"showClearButton\": true}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "ContactName",
|
|
||||||
"CaptionName": "App.Listform.ListformField.ContactName",
|
|
||||||
"TurkishCaption": "İlgili Kişi",
|
|
||||||
"EnglishCaption": "Contact",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"maxLength\": 200}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "Email",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Email",
|
|
||||||
"TurkishCaption": "E-posta",
|
|
||||||
"EnglishCaption": "Email",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"maxLength\": 200, \"mode\": \"email\"}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "Phone",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Phone",
|
|
||||||
"TurkishCaption": "Telefon",
|
|
||||||
"EnglishCaption": "Phone",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"maxLength\": 50}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"SubForms": [],
|
|
||||||
"Widgets": []
|
|
||||||
},
|
|
||||||
"IsDeletedField": true,
|
|
||||||
"IsCreatedField": true,
|
|
||||||
"InsertedRecords": {
|
|
||||||
"LanguageKeys": [
|
|
||||||
"App.Prj.Customers"
|
|
||||||
],
|
|
||||||
"PermissionGroupNames": [],
|
|
||||||
"PermissionNames": [
|
|
||||||
"App.Prj.Customers",
|
|
||||||
"App.Prj.Customers.Create",
|
|
||||||
"App.Prj.Customers.Update",
|
|
||||||
"App.Prj.Customers.Delete",
|
|
||||||
"App.Prj.Customers.Export",
|
|
||||||
"App.Prj.Customers.Import",
|
|
||||||
"App.Prj.Customers.Note"
|
|
||||||
],
|
|
||||||
"MenuCodes": [
|
|
||||||
"App.Prj.Customers"
|
|
||||||
],
|
|
||||||
"DataSourceCodes": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,421 +0,0 @@
|
||||||
{
|
|
||||||
"Wizard": {
|
|
||||||
"ComponentKind": 0,
|
|
||||||
"WizardName": "Tasks",
|
|
||||||
"ListFormCode": "App.Prj.Tasks",
|
|
||||||
"MenuCode": "App.Prj.Tasks",
|
|
||||||
"MenuOrder": 1,
|
|
||||||
"CreateMenu": true,
|
|
||||||
"MenuUrl": "/admin/list/App.Prj.Tasks",
|
|
||||||
"CustomComponentName": "",
|
|
||||||
"IsTenant": true,
|
|
||||||
"IsBranch": false,
|
|
||||||
"IsOrganizationUnit": false,
|
|
||||||
"AllowAdding": true,
|
|
||||||
"AllowUpdating": true,
|
|
||||||
"AllowDeleting": true,
|
|
||||||
"AllowDetail": true,
|
|
||||||
"ConfirmDelete": true,
|
|
||||||
"DefaultLayout": "grid",
|
|
||||||
"Grid": true,
|
|
||||||
"Card": true,
|
|
||||||
"Pivot": false,
|
|
||||||
"Chart": false,
|
|
||||||
"Tree": false,
|
|
||||||
"Gantt": true,
|
|
||||||
"Scheduler": false,
|
|
||||||
"Todo": true,
|
|
||||||
"LanguageTextMenuEn": "Tasks",
|
|
||||||
"LanguageTextMenuTr": "Görevler",
|
|
||||||
"LanguageTextTitleEn": "Tasks",
|
|
||||||
"LanguageTextTitleTr": "Görevler",
|
|
||||||
"LanguageTextDescEn": "Task card and tracking",
|
|
||||||
"LanguageTextDescTr": "Görev kartı ve takibi",
|
|
||||||
"LanguageTextMenuParentEn": "Project",
|
|
||||||
"LanguageTextMenuParentTr": "Proje",
|
|
||||||
"PermissionGroupName": "App.Prj",
|
|
||||||
"PermissionGroupDisplayNameEn": "Project",
|
|
||||||
"PermissionGroupDisplayNameTr": "Proje",
|
|
||||||
"MenuParentCode": "App.Prj",
|
|
||||||
"MenuParentModuleId": "Prj",
|
|
||||||
"MenuParentIcon": "FcTimeline",
|
|
||||||
"MenuIcon": "FcTodoList",
|
|
||||||
"DataSourceCode": "Default",
|
|
||||||
"DataSourceConnectionString": "",
|
|
||||||
"SelectCommandType": 1,
|
|
||||||
"SelectCommand": "Prj_T_Task",
|
|
||||||
"KeyFieldName": "Id",
|
|
||||||
"KeyFieldDbSourceType": 9,
|
|
||||||
"TreeOptionDto": {
|
|
||||||
"KeyExpr": "",
|
|
||||||
"ParentIdExpr": "",
|
|
||||||
"HasItemsExpr": "",
|
|
||||||
"RootValue": "",
|
|
||||||
"ExpandedRowKeys": [],
|
|
||||||
"AutoExpandAll": false,
|
|
||||||
"RecursiveSelection": false,
|
|
||||||
"TitleExpr": null,
|
|
||||||
"StartExpr": null,
|
|
||||||
"EndExpr": null,
|
|
||||||
"ProgressExpr": null
|
|
||||||
},
|
|
||||||
"GanttOptionDto": {
|
|
||||||
"KeyExpr": "Id",
|
|
||||||
"ParentIdExpr": "ParentTaskId",
|
|
||||||
"HasItemsExpr": "",
|
|
||||||
"RootValue": "",
|
|
||||||
"ExpandedRowKeys": [],
|
|
||||||
"AutoExpandAll": false,
|
|
||||||
"RecursiveSelection": false,
|
|
||||||
"TitleExpr": "Name",
|
|
||||||
"ScaleType": "weeks",
|
|
||||||
"StartExpr": "PlannedStartDate",
|
|
||||||
"EndExpr": "PlannedEndDate",
|
|
||||||
"ProgressExpr": "ProgressPercent",
|
|
||||||
"AllowEditing": false,
|
|
||||||
"AllowTaskAdding": false,
|
|
||||||
"AllowTaskUpdating": false,
|
|
||||||
"AllowTaskResourceUpdating": false,
|
|
||||||
"AllowTaskDeleting": false,
|
|
||||||
"AllowDependencyAdding": false,
|
|
||||||
"AllowDependencyDeleting": false,
|
|
||||||
"AllowResourceAdding": false,
|
|
||||||
"AllowResourceUpdating": false,
|
|
||||||
"AllowResourceDeleting": false
|
|
||||||
},
|
|
||||||
"SchedulerOptionDto": {
|
|
||||||
"TextExpr": "",
|
|
||||||
"UserNameExpr": null,
|
|
||||||
"DescriptionExpr": null,
|
|
||||||
"StartDateExpr": "",
|
|
||||||
"EndDateExpr": "",
|
|
||||||
"AllDayExpr": "",
|
|
||||||
"RecurrenceRuleExpr": "",
|
|
||||||
"RecurrenceExceptionExpr": "",
|
|
||||||
"StartDayHour": 8,
|
|
||||||
"EndDayHour": 20,
|
|
||||||
"DefaultView": "week",
|
|
||||||
"ShowAllDayPanel": true,
|
|
||||||
"CellDuration": 30,
|
|
||||||
"FirstDayOfWeek": 1,
|
|
||||||
"CrossScrollingEnabled": false,
|
|
||||||
"AllowResizing": false,
|
|
||||||
"AllowDragging": false,
|
|
||||||
"AllowDeleting": false,
|
|
||||||
"AllowEditing": false,
|
|
||||||
"AllowAdding": false
|
|
||||||
},
|
|
||||||
"TodoOptionDto": {
|
|
||||||
"TitleExpr": "Name",
|
|
||||||
"StatusExpr": "Status",
|
|
||||||
"DescriptionExpr": "Description",
|
|
||||||
"DueDateExpr": "PlannedEndDate",
|
|
||||||
"TagExpr": "TagIds",
|
|
||||||
"AssigneeExpr": "AssigneeIds",
|
|
||||||
"PriorityExpr": "",
|
|
||||||
"CompletedExpr": "",
|
|
||||||
"OrderExpr": "",
|
|
||||||
"StatusOrder": "Taslak, Devam Ediyor, Tamamlandı, İptal",
|
|
||||||
"AllowDragging": true
|
|
||||||
},
|
|
||||||
"WorkflowDto": {
|
|
||||||
"ApprovalUserFieldName": "",
|
|
||||||
"ApprovalDateFieldName": "",
|
|
||||||
"ApprovalStatusFieldName": "",
|
|
||||||
"ApprovalDescriptionFieldName": "",
|
|
||||||
"ApprovalIsFilterUserName": false,
|
|
||||||
"ApprovalIsResetWorkflow": false,
|
|
||||||
"Criteria": []
|
|
||||||
},
|
|
||||||
"Groups": [
|
|
||||||
{
|
|
||||||
"Caption": "Genel",
|
|
||||||
"ColCount": 2,
|
|
||||||
"Items": [
|
|
||||||
{
|
|
||||||
"FieldName": "Id",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Id",
|
|
||||||
"TurkishCaption": "Kimlik",
|
|
||||||
"EnglishCaption": "Id",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 9,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": false,
|
|
||||||
"ColSpan": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "Name",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Name",
|
|
||||||
"TurkishCaption": "Görev Adı",
|
|
||||||
"EnglishCaption": "Task Name",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": true,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 2,
|
|
||||||
"EditorOptions": "{\"maxLength\": 300, \"showClearButton\": true}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "MilestoneId",
|
|
||||||
"CaptionName": "App.Listform.ListformField.MilestoneId",
|
|
||||||
"TurkishCaption": "Dönüm Noktası",
|
|
||||||
"EnglishCaption": "Milestone",
|
|
||||||
"EditorType": "dxSelectBox",
|
|
||||||
"DbSourceType": 9,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"searchEnabled\": true, \"showClearButton\": true}",
|
|
||||||
"LookupDataSourceType": 2,
|
|
||||||
"LookupQuery": "SELECT Id AS [Key], Name AS [Name] FROM dbo.Prj_T_Milestone WHERE IsDeleted = 0 ORDER BY Name",
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "Status",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Status",
|
|
||||||
"TurkishCaption": "Durum",
|
|
||||||
"EnglishCaption": "Status",
|
|
||||||
"EditorType": "dxSelectBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": true,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"LookupDataSourceType": 1,
|
|
||||||
"LookupQuery": "[{\"key\": \"Taslak\", \"name\": \"Taslak\"}, {\"key\": \"Devam Ediyor\", \"name\": \"Devam Ediyor\"}, {\"key\": \"Tamamlandı\", \"name\": \"Tamamlandı\"}, {\"key\": \"İptal\", \"name\": \"İptal\"}]",
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "AssigneeIds",
|
|
||||||
"CaptionName": "App.Listform.ListformField.AssigneeIds",
|
|
||||||
"TurkishCaption": "Atananlar",
|
|
||||||
"EnglishCaption": "Assignees",
|
|
||||||
"EditorType": "dxTagBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"showSelectionControls\": true, \"maxDisplayedTags\": 3, \"searchEnabled\": true, \"multiline\": true}",
|
|
||||||
"LookupDataSourceType": 2,
|
|
||||||
"LookupQuery": "SELECT Id AS [Key], UserName AS [Name] FROM dbo.AbpUsers WHERE IsDeleted = 0 ORDER BY UserName",
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "TagIds",
|
|
||||||
"CaptionName": "App.Listform.ListformField.TagIds",
|
|
||||||
"TurkishCaption": "Etiketler",
|
|
||||||
"EnglishCaption": "Tags",
|
|
||||||
"EditorType": "dxTagBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"showSelectionControls\": true, \"maxDisplayedTags\": 3, \"searchEnabled\": true, \"multiline\": true}",
|
|
||||||
"LookupDataSourceType": 2,
|
|
||||||
"LookupQuery": "SELECT Id AS [Key], Name AS [Name] FROM dbo.Prj_T_Tag WHERE IsDeleted = 0 ORDER BY Name",
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "CustomerId",
|
|
||||||
"CaptionName": "App.Listform.ListformField.CustomerId",
|
|
||||||
"TurkishCaption": "Müşteri",
|
|
||||||
"EnglishCaption": "Customer",
|
|
||||||
"EditorType": "dxSelectBox",
|
|
||||||
"DbSourceType": 9,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"searchEnabled\": true, \"showClearButton\": true}",
|
|
||||||
"LookupDataSourceType": 2,
|
|
||||||
"LookupQuery": "SELECT Id AS [Key], Name AS [Name] FROM dbo.Prj_T_Customer WHERE IsDeleted = 0 ORDER BY Name",
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "CustomerContact",
|
|
||||||
"CaptionName": "App.Listform.ListformField.CustomerContact",
|
|
||||||
"TurkishCaption": "İlgili Kişi",
|
|
||||||
"EnglishCaption": "Contact",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"maxLength\": 200}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "PlannedHours",
|
|
||||||
"CaptionName": "App.Listform.ListformField.PlannedHours",
|
|
||||||
"TurkishCaption": "İlk Planlanan Saatler",
|
|
||||||
"EnglishCaption": "Planned Hours",
|
|
||||||
"EditorType": "dxNumberBox",
|
|
||||||
"DbSourceType": 7,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"format\": {\"type\": \"fixedPoint\", \"precision\": 2}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "ProgressPercent",
|
|
||||||
"CaptionName": "App.Listform.ListformField.ProgressPercent",
|
|
||||||
"TurkishCaption": "İlerleme (%)",
|
|
||||||
"EnglishCaption": "Progress (%)",
|
|
||||||
"EditorType": "dxNumberBox",
|
|
||||||
"DbSourceType": 7,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"format\": {\"type\": \"fixedPoint\", \"precision\": 2}, \"min\": 0, \"max\": 100}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "SalesOrderLine",
|
|
||||||
"CaptionName": "App.Listform.ListformField.SalesOrderLine",
|
|
||||||
"TurkishCaption": "Satış Sipariş Satırı",
|
|
||||||
"EnglishCaption": "Sales Order Line",
|
|
||||||
"EditorType": "dxTextBox",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 2,
|
|
||||||
"EditorOptions": "{\"maxLength\": 300}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "PlannedStartDate",
|
|
||||||
"CaptionName": "App.Listform.ListformField.PlannedStartDate",
|
|
||||||
"TurkishCaption": "Planlanan Tarih",
|
|
||||||
"EnglishCaption": "Planned Date",
|
|
||||||
"EditorType": "dxDateBox",
|
|
||||||
"DbSourceType": 6,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"type\": \"datetime\", \"displayFormat\": \"dd.MM.yyyy HH:mm\", \"useMaskBehavior\": true}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "PlannedEndDate",
|
|
||||||
"CaptionName": "App.Listform.ListformField.PlannedEndDate",
|
|
||||||
"TurkishCaption": "Planlanan Bitiş",
|
|
||||||
"EnglishCaption": "Planned End",
|
|
||||||
"EditorType": "dxDateBox",
|
|
||||||
"DbSourceType": 6,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"type\": \"datetime\", \"displayFormat\": \"dd.MM.yyyy HH:mm\", \"useMaskBehavior\": true}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "ParentTaskId",
|
|
||||||
"CaptionName": "App.Listform.ListformField.ParentTaskId",
|
|
||||||
"TurkishCaption": "Üst Görev",
|
|
||||||
"EnglishCaption": "Parent Task",
|
|
||||||
"EditorType": "dxSelectBox",
|
|
||||||
"DbSourceType": 9,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"searchEnabled\": true, \"showClearButton\": true}",
|
|
||||||
"LookupDataSourceType": 2,
|
|
||||||
"LookupQuery": "SELECT Id AS [Key], Name AS [Name] FROM dbo.Prj_T_Task WHERE IsDeleted = 0 ORDER BY Name",
|
|
||||||
"ValueExpr": "Key",
|
|
||||||
"DisplayExpr": "Name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"FieldName": "IsFavorite",
|
|
||||||
"CaptionName": "App.Listform.ListformField.IsFavorite",
|
|
||||||
"TurkishCaption": "Favori",
|
|
||||||
"EnglishCaption": "Favorite",
|
|
||||||
"EditorType": "dxCheckBox",
|
|
||||||
"DbSourceType": 3,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Caption": "Açıklama",
|
|
||||||
"ColCount": 1,
|
|
||||||
"Items": [
|
|
||||||
{
|
|
||||||
"FieldName": "Description",
|
|
||||||
"CaptionName": "App.Listform.ListformField.Description",
|
|
||||||
"TurkishCaption": "Açıklama",
|
|
||||||
"EnglishCaption": "Description",
|
|
||||||
"EditorType": "dxHtmlEditor",
|
|
||||||
"DbSourceType": 16,
|
|
||||||
"IsRequired": false,
|
|
||||||
"IncludeInEditingForm": true,
|
|
||||||
"ColSpan": 1,
|
|
||||||
"EditorOptions": "{\"valueType\": \"html\", \"height\": 320, \"toolbar\": {\"multiline\": false, \"items\": [\"undo\", \"redo\", \"separator\", \"bold\", \"italic\", \"strike\", \"underline\", \"separator\", \"alignLeft\", \"alignCenter\", \"alignRight\", \"alignJustify\", \"separator\", \"orderedList\", \"bulletList\", \"separator\", \"header\", \"separator\", \"color\", \"background\", \"separator\", \"link\", \"image\", \"separator\", \"clear\", \"codeBlock\", \"blockquote\"]}}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"SubForms": [
|
|
||||||
{
|
|
||||||
"TabType": "List",
|
|
||||||
"TabTitle": "App.Prj.TaskTimesheets",
|
|
||||||
"Code": "App.Prj.TaskTimesheets",
|
|
||||||
"IsRefresh": true,
|
|
||||||
"Relation": [
|
|
||||||
{
|
|
||||||
"ParentFieldName": "Id",
|
|
||||||
"ChildFieldName": "TaskId",
|
|
||||||
"DbType": 9
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"TabType": "List",
|
|
||||||
"TabTitle": "App.Prj.TaskSubTasks",
|
|
||||||
"Code": "App.Prj.TaskSubTasks",
|
|
||||||
"IsRefresh": true,
|
|
||||||
"Relation": [
|
|
||||||
{
|
|
||||||
"ParentFieldName": "Id",
|
|
||||||
"ChildFieldName": "ParentTaskId",
|
|
||||||
"DbType": 9
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"TabType": "List",
|
|
||||||
"TabTitle": "App.Prj.TaskDependencies",
|
|
||||||
"Code": "App.Prj.TaskDependencies",
|
|
||||||
"IsRefresh": true,
|
|
||||||
"Relation": [
|
|
||||||
{
|
|
||||||
"ParentFieldName": "Id",
|
|
||||||
"ChildFieldName": "TaskId",
|
|
||||||
"DbType": 9
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Widgets": []
|
|
||||||
},
|
|
||||||
"IsDeletedField": true,
|
|
||||||
"IsCreatedField": true,
|
|
||||||
"InsertedRecords": {
|
|
||||||
"LanguageKeys": [
|
|
||||||
"App.Prj.Tasks"
|
|
||||||
],
|
|
||||||
"PermissionGroupNames": [],
|
|
||||||
"PermissionNames": [
|
|
||||||
"App.Prj.Tasks",
|
|
||||||
"App.Prj.Tasks.Create",
|
|
||||||
"App.Prj.Tasks.Update",
|
|
||||||
"App.Prj.Tasks.Delete",
|
|
||||||
"App.Prj.Tasks.Export",
|
|
||||||
"App.Prj.Tasks.Import",
|
|
||||||
"App.Prj.Tasks.Note"
|
|
||||||
],
|
|
||||||
"MenuCodes": [
|
|
||||||
"App.Prj.Tasks"
|
|
||||||
],
|
|
||||||
"DataSourceCodes": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1497
configs/seeds/host/wizard/Approval.json
Normal file
1497
configs/seeds/host/wizard/Approval.json
Normal file
File diff suppressed because it is too large
Load diff
1311
configs/seeds/host/wizard/Customers.json
Normal file
1311
configs/seeds/host/wizard/Customers.json
Normal file
File diff suppressed because it is too large
Load diff
1179
configs/seeds/host/wizard/Milestones.json
Normal file
1179
configs/seeds/host/wizard/Milestones.json
Normal file
File diff suppressed because it is too large
Load diff
1048
configs/seeds/host/wizard/Tags.json
Normal file
1048
configs/seeds/host/wizard/Tags.json
Normal file
File diff suppressed because it is too large
Load diff
1190
configs/seeds/host/wizard/TaskDependencies.json
Normal file
1190
configs/seeds/host/wizard/TaskDependencies.json
Normal file
File diff suppressed because it is too large
Load diff
1726
configs/seeds/host/wizard/TaskSubTasks.json
Normal file
1726
configs/seeds/host/wizard/TaskSubTasks.json
Normal file
File diff suppressed because it is too large
Load diff
1469
configs/seeds/host/wizard/TaskTimesheets.json
Normal file
1469
configs/seeds/host/wizard/TaskTimesheets.json
Normal file
File diff suppressed because it is too large
Load diff
2842
configs/seeds/host/wizard/Tasks.json
Normal file
2842
configs/seeds/host/wizard/Tasks.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,7 @@
|
||||||
|
import { ColumnFormatEditDto } from '@/proxy/admin/list-form-field/models'
|
||||||
import {
|
import {
|
||||||
GanttOptionDto,
|
GanttOptionDto,
|
||||||
|
GridOptionsEditDto,
|
||||||
SchedulerOptionDto,
|
SchedulerOptionDto,
|
||||||
SubFormDto,
|
SubFormDto,
|
||||||
TodoOptionDto,
|
TodoOptionDto,
|
||||||
|
|
@ -43,6 +45,11 @@ export interface ListFormWizardDto {
|
||||||
editFileName?: string
|
editFileName?: string
|
||||||
|
|
||||||
wizardName: string
|
wizardName: string
|
||||||
|
/**
|
||||||
|
* Wizard'ın ilk deploy edildiği an. Dosya adı tarih taşımadığı için seeder dosyaları bu
|
||||||
|
* damgaya göre sıralar; sunucu üretir, istemci göndermez.
|
||||||
|
*/
|
||||||
|
seededAt?: string
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
menuCode: string
|
menuCode: string
|
||||||
menuOrder: number
|
menuOrder: number
|
||||||
|
|
@ -168,6 +175,10 @@ export interface WizardSeedFileWizardDto extends Omit<ListFormWizardDto, 'groups
|
||||||
|
|
||||||
export interface WizardSeedFileDto {
|
export interface WizardSeedFileDto {
|
||||||
wizard: WizardSeedFileWizardDto
|
wizard: WizardSeedFileWizardDto
|
||||||
|
/** Wizard'ın ürettiği ekranın ListForm sözleşmesi; tasarımcıdaki değişiklikler buraya yazılır. */
|
||||||
|
listForm?: GridOptionsEditDto
|
||||||
|
/** Ekranın alanları; ListForm detayındaki alan tanımlarıyla aynı sözleşme. */
|
||||||
|
fields?: ColumnFormatEditDto[]
|
||||||
isDeletedField: boolean
|
isDeletedField: boolean
|
||||||
isCreatedField: boolean
|
isCreatedField: boolean
|
||||||
insertedRecords: WizardInsertedRecordsDto
|
insertedRecords: WizardInsertedRecordsDto
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import {
|
||||||
ColumnFormatEditDto,
|
ColumnFormatEditDto,
|
||||||
CopyListFormFieldColumnDto,
|
CopyListFormFieldColumnDto,
|
||||||
} from '@/proxy/admin/list-form-field/models'
|
} from '@/proxy/admin/list-form-field/models'
|
||||||
import { LanguageInfo } from '@/proxy/config/models'
|
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import { useStoreState } from '@/store'
|
import { useStoreState } from '@/store'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
|
@ -39,7 +38,6 @@ import { FaFileMedical, FaCopy, FaEyeSlash, FaMinus, FaTimes, FaTable } from 're
|
||||||
import { number, object, string } from 'yup'
|
import { number, object, string } from 'yup'
|
||||||
import FormFieldEdit from './FormFieldEdit'
|
import FormFieldEdit from './FormFieldEdit'
|
||||||
import { dbSourceTypeOptions, sqlDataTypeToDbType } from '../options'
|
import { dbSourceTypeOptions, sqlDataTypeToDbType } from '../options'
|
||||||
import { IdentityRoleDto, IdentityUserDto } from '@/proxy/admin/models'
|
|
||||||
import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
||||||
import type { DatabaseColumnDto } from '@/proxy/sql-query-manager/models'
|
import type { DatabaseColumnDto } from '@/proxy/sql-query-manager/models'
|
||||||
import type { EditingFormDto } from '@/proxy/form/models'
|
import type { EditingFormDto } from '@/proxy/form/models'
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,10 @@ import { Field, FieldProps, FormikErrors, FormikTouched } from 'formik'
|
||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import CreatableSelect from 'react-select/creatable'
|
import CreatableSelect from 'react-select/creatable'
|
||||||
import {
|
import {
|
||||||
|
FaArrowDown,
|
||||||
FaArrowLeft,
|
FaArrowLeft,
|
||||||
FaArrowRight,
|
FaArrowRight,
|
||||||
|
FaArrowUp,
|
||||||
FaChevronDown,
|
FaChevronDown,
|
||||||
FaChevronRight,
|
FaChevronRight,
|
||||||
FaEdit,
|
FaEdit,
|
||||||
|
|
@ -47,6 +49,14 @@ interface TreeNodeProps {
|
||||||
saving: boolean
|
saving: boolean
|
||||||
onStartEdit: (node: MenuTreeNode & { id?: string }) => void
|
onStartEdit: (node: MenuTreeNode & { id?: string }) => void
|
||||||
onDelete: (node: MenuTreeNode & { id?: string }) => void
|
onDelete: (node: MenuTreeNode & { id?: string }) => void
|
||||||
|
/** Aynı seviyedeki menüler; sıra değişimi bu liste üzerinden hesaplanır. */
|
||||||
|
siblings: (MenuTreeNode & { id?: string })[]
|
||||||
|
index: number
|
||||||
|
onMove: (
|
||||||
|
siblings: (MenuTreeNode & { id?: string })[],
|
||||||
|
index: number,
|
||||||
|
direction: -1 | 1,
|
||||||
|
) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function TreeNode({
|
function TreeNode({
|
||||||
|
|
@ -59,6 +69,9 @@ function TreeNode({
|
||||||
saving,
|
saving,
|
||||||
onStartEdit,
|
onStartEdit,
|
||||||
onDelete,
|
onDelete,
|
||||||
|
siblings,
|
||||||
|
index,
|
||||||
|
onMove,
|
||||||
}: TreeNodeProps) {
|
}: TreeNodeProps) {
|
||||||
const hasChildren = node.children.length > 0
|
const hasChildren = node.children.length > 0
|
||||||
const isExpanded = expanded.has(node.code)
|
const isExpanded = expanded.has(node.code)
|
||||||
|
|
@ -114,6 +127,34 @@ function TreeNode({
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="shrink-0 flex items-center gap-0 opacity-0 group-hover:opacity-100 transition-opacity">
|
<span className="shrink-0 flex items-center gap-0 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
disabled={saving || index === 0}
|
||||||
|
title={translate('::App.Platform.MoveUp')}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
onMove(siblings, index, -1)
|
||||||
|
}}
|
||||||
|
size="xs"
|
||||||
|
variant="plain"
|
||||||
|
shape="circle"
|
||||||
|
icon={<FaArrowUp className="text-xs" />}
|
||||||
|
className={`!h-6 !w-6 !px-0 hover:!bg-transparent ${isSelected ? 'text-indigo-200 hover:text-white' : 'text-gray-300 hover:text-indigo-500'}`}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
disabled={saving || index === siblings.length - 1}
|
||||||
|
title={translate('::App.Platform.MoveDown')}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
onMove(siblings, index, 1)
|
||||||
|
}}
|
||||||
|
size="xs"
|
||||||
|
variant="plain"
|
||||||
|
shape="circle"
|
||||||
|
icon={<FaArrowDown className="text-xs" />}
|
||||||
|
className={`!h-6 !w-6 !px-0 hover:!bg-transparent ${isSelected ? 'text-indigo-200 hover:text-white' : 'text-gray-300 hover:text-indigo-500'}`}
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
|
|
@ -146,7 +187,7 @@ function TreeNode({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isExpanded &&
|
{isExpanded &&
|
||||||
node.children.map((child) => (
|
node.children.map((child, childIndex) => (
|
||||||
<TreeNode
|
<TreeNode
|
||||||
key={child.code}
|
key={child.code}
|
||||||
node={child as MenuTreeNode & { id?: string }}
|
node={child as MenuTreeNode & { id?: string }}
|
||||||
|
|
@ -158,6 +199,9 @@ function TreeNode({
|
||||||
saving={saving}
|
saving={saving}
|
||||||
onStartEdit={onStartEdit}
|
onStartEdit={onStartEdit}
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
|
siblings={node.children as (MenuTreeNode & { id?: string })[]}
|
||||||
|
index={childIndex}
|
||||||
|
onMove={onMove}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -237,6 +281,50 @@ function MenuTreeInline({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kardeşler arasında sıra değiştirir. Yeni sıra numaraları kardeşlerin mevcut değerlerinden
|
||||||
|
* alınır; böylece görünmeyen (link) menülerin araya giren sıraları bozulmaz.
|
||||||
|
*/
|
||||||
|
const handleMove = async (
|
||||||
|
siblings: (MenuTreeNode & { id?: string })[],
|
||||||
|
index: number,
|
||||||
|
direction: -1 | 1,
|
||||||
|
) => {
|
||||||
|
const targetIndex = index + direction
|
||||||
|
if (targetIndex < 0 || targetIndex >= siblings.length) return
|
||||||
|
|
||||||
|
const itemByCode = new Map(rawItems.map((item) => [item.code, item]))
|
||||||
|
const current = siblings.map((s) => itemByCode.get(s.code))
|
||||||
|
if (current.some((item) => !item?.id)) return
|
||||||
|
|
||||||
|
const reordered = current as MenuItem[]
|
||||||
|
const moved = [...reordered]
|
||||||
|
const [item] = moved.splice(index, 1)
|
||||||
|
moved.splice(targetIndex, 0, item)
|
||||||
|
|
||||||
|
const slots = reordered.map((i) => i.order ?? 0).sort((a, b) => a - b)
|
||||||
|
let previous = Number.NEGATIVE_INFINITY
|
||||||
|
const payload = moved.reduce<MenuItem[]>((acc, entry, i) => {
|
||||||
|
const order = Math.max(slots[i], previous + 1)
|
||||||
|
previous = order
|
||||||
|
if (entry.order !== order) acc.push({ ...entry, order })
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
if (payload.length === 0) return
|
||||||
|
|
||||||
|
setSaving(true)
|
||||||
|
try {
|
||||||
|
await menuService.updateAll(payload)
|
||||||
|
onReload()
|
||||||
|
} catch (e: any) {
|
||||||
|
toast.push(<Notification title={e.message} type="danger" />, {
|
||||||
|
placement: 'bottom-end',
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setSaving(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Menü ağacı her render'da yeniden dolaşılmasın; id eşlemesi tek haritadan yapılır.
|
// Menü ağacı her render'da yeniden dolaşılmasın; id eşlemesi tek haritadan yapılır.
|
||||||
const enrichedNodes = useMemo(() => {
|
const enrichedNodes = useMemo(() => {
|
||||||
const idByCode = new Map(rawItems.map((item) => [item.code, item.id]))
|
const idByCode = new Map(rawItems.map((item) => [item.code, item.id]))
|
||||||
|
|
@ -254,6 +342,7 @@ function MenuTreeInline({
|
||||||
saving,
|
saving,
|
||||||
onStartEdit: onEditMenu,
|
onStartEdit: onEditMenu,
|
||||||
onDelete: handleDelete,
|
onDelete: handleDelete,
|
||||||
|
onMove: handleMove,
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -269,13 +358,15 @@ function MenuTreeInline({
|
||||||
{translate('::App.WizardStep1.NoMenusAvailable')}
|
{translate('::App.WizardStep1.NoMenusAvailable')}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
enrichedNodes.map((node) => (
|
enrichedNodes.map((node, index) => (
|
||||||
<TreeNode
|
<TreeNode
|
||||||
key={node.code}
|
key={node.code}
|
||||||
node={node}
|
node={node}
|
||||||
depth={0}
|
depth={0}
|
||||||
selectedCode={value}
|
selectedCode={value}
|
||||||
onSelect={onChange}
|
onSelect={onChange}
|
||||||
|
siblings={enrichedNodes}
|
||||||
|
index={index}
|
||||||
{...sharedNodeProps}
|
{...sharedNodeProps}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,14 @@ import {
|
||||||
FaTasks,
|
FaTasks,
|
||||||
FaChartBar,
|
FaChartBar,
|
||||||
FaRegCalendarAlt,
|
FaRegCalendarAlt,
|
||||||
|
FaToggleOn,
|
||||||
|
FaToggleOff,
|
||||||
} from 'react-icons/fa'
|
} from 'react-icons/fa'
|
||||||
import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
||||||
import { getMenus } from '@/services/menu.service'
|
import { getMenus } from '@/services/menu.service'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { CascadeBehavior, SqlTableRelation, RelationshipType } from '@/proxy/developerKit/models'
|
import { CascadeBehavior, SqlTableRelation, RelationshipType } from '@/proxy/developerKit/models'
|
||||||
|
import type { CrudEndpoint } from '@/proxy/developerKit/models'
|
||||||
import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
|
import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
|
||||||
import { MenuItem } from '@/proxy/menus/menu'
|
import { MenuItem } from '@/proxy/menus/menu'
|
||||||
import {
|
import {
|
||||||
|
|
@ -45,6 +48,7 @@ import {
|
||||||
CRUD_ENDPOINT_PERMISSION,
|
CRUD_ENDPOINT_PERMISSION,
|
||||||
CRUD_OPERATION_TYPES,
|
CRUD_OPERATION_TYPES,
|
||||||
type CrudOperationType,
|
type CrudOperationType,
|
||||||
|
METHOD_COLOR,
|
||||||
getErrorMessage,
|
getErrorMessage,
|
||||||
getOperationMeta,
|
getOperationMeta,
|
||||||
toEntityName,
|
toEntityName,
|
||||||
|
|
@ -1333,6 +1337,9 @@ const SqlTableDesignerDialog = ({
|
||||||
const [crudOperations, setCrudOperations] = useState<CrudOperationType[]>([
|
const [crudOperations, setCrudOperations] = useState<CrudOperationType[]>([
|
||||||
...CRUD_OPERATION_TYPES,
|
...CRUD_OPERATION_TYPES,
|
||||||
])
|
])
|
||||||
|
/** Duzenlenen tablonun kayitli endpoint'leri; adim mevcut durumu gosterir. */
|
||||||
|
const [existingCrudEndpoints, setExistingCrudEndpoints] = useState<CrudEndpoint[]>([])
|
||||||
|
const [crudLoading, setCrudLoading] = useState(false)
|
||||||
const [columns, setColumns] = useState<ColumnDefinition[]>([createEmptyColumn()])
|
const [columns, setColumns] = useState<ColumnDefinition[]>([createEmptyColumn()])
|
||||||
const [originalColumns, setOriginalColumns] = useState<ColumnDefinition[]>([])
|
const [originalColumns, setOriginalColumns] = useState<ColumnDefinition[]>([])
|
||||||
const [colsLoading, setColsLoading] = useState(false)
|
const [colsLoading, setColsLoading] = useState(false)
|
||||||
|
|
@ -1418,6 +1425,24 @@ const SqlTableDesignerDialog = ({
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Edit mode: kayitli CRUD endpoint'leri okunur; varsa adim mevcut durumla acilir.
|
||||||
|
if (initialTableData && canManageCrudEndpoints) {
|
||||||
|
setCrudLoading(true)
|
||||||
|
developerKitService
|
||||||
|
.getEndpointsByEntity(toEntityName(initialTableData.tableName))
|
||||||
|
.then((endpoints) => {
|
||||||
|
setExistingCrudEndpoints(endpoints)
|
||||||
|
if (endpoints.length === 0) return
|
||||||
|
setGenerateCrud(true)
|
||||||
|
const active = endpoints
|
||||||
|
.filter((e) => e.isActive)
|
||||||
|
.map((e) => e.operationType as CrudOperationType)
|
||||||
|
setCrudOperations(CRUD_OPERATION_TYPES.filter((op) => active.includes(op)))
|
||||||
|
})
|
||||||
|
.catch(() => setExistingCrudEndpoints([]))
|
||||||
|
.finally(() => setCrudLoading(false))
|
||||||
|
}
|
||||||
|
|
||||||
// Edit mode: load table's existing columns + FK constraints
|
// Edit mode: load table's existing columns + FK constraints
|
||||||
if (initialTableData && dataSource) {
|
if (initialTableData && dataSource) {
|
||||||
setColsLoading(true)
|
setColsLoading(true)
|
||||||
|
|
@ -1541,7 +1566,7 @@ const SqlTableDesignerDialog = ({
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
.finally(() => setIndexesLoading(false))
|
.finally(() => setIndexesLoading(false))
|
||||||
}
|
}
|
||||||
}, [isOpen, dataSource, initialTableData])
|
}, [isOpen, dataSource, initialTableData, canManageCrudEndpoints])
|
||||||
|
|
||||||
const generatedSql = useMemo(
|
const generatedSql = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
|
@ -2126,6 +2151,7 @@ const SqlTableDesignerDialog = ({
|
||||||
setTemplatesOpen(false)
|
setTemplatesOpen(false)
|
||||||
setGenerateCrud(false)
|
setGenerateCrud(false)
|
||||||
setCrudOperations([...CRUD_OPERATION_TYPES])
|
setCrudOperations([...CRUD_OPERATION_TYPES])
|
||||||
|
setExistingCrudEndpoints([])
|
||||||
onClose()
|
onClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3343,6 +3369,43 @@ const SqlTableDesignerDialog = ({
|
||||||
|
|
||||||
const renderCrudEndpoints = () => (
|
const renderCrudEndpoints = () => (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
|
{crudLoading && (
|
||||||
|
<p className="mb-0 text-xs text-gray-400">{translate('::App.Platform.Loading')}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{existingCrudEndpoints.length > 0 && (
|
||||||
|
<div className="rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-900 dark:bg-blue-900/20">
|
||||||
|
<span className="block text-sm font-medium">
|
||||||
|
{translate('::App.SqlQueryManager.ExistingCrudEndpoints')}
|
||||||
|
</span>
|
||||||
|
<span className="mb-2 block text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
{translate('::App.SqlQueryManager.ExistingCrudEndpointsHint')}
|
||||||
|
</span>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
{existingCrudEndpoints.map((endpoint) => (
|
||||||
|
<div key={endpoint.id} className="flex items-center gap-2 text-xs">
|
||||||
|
<span
|
||||||
|
className={`rounded border px-1.5 py-0.5 font-mono ${
|
||||||
|
METHOD_COLOR[endpoint.method] ||
|
||||||
|
'border-gray-200 bg-gray-100 text-gray-700 dark:border-gray-700 dark:bg-gray-700 dark:text-gray-200'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{endpoint.method}
|
||||||
|
</span>
|
||||||
|
<span className="font-mono text-gray-600 dark:text-gray-300">{endpoint.path}</span>
|
||||||
|
<span className="ml-auto">
|
||||||
|
{endpoint.isActive ? (
|
||||||
|
<FaToggleOn className="text-base text-green-500" />
|
||||||
|
) : (
|
||||||
|
<FaToggleOff className="text-base text-gray-300 dark:text-gray-600" />
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<label className="flex items-start gap-3 cursor-pointer rounded-lg border p-3 dark:border-gray-700">
|
<label className="flex items-start gap-3 cursor-pointer rounded-lg border p-3 dark:border-gray-700">
|
||||||
<Checkbox checked={generateCrud} onChange={(checked) => setGenerateCrud(checked)} />
|
<Checkbox checked={generateCrud} onChange={(checked) => setGenerateCrud(checked)} />
|
||||||
<span>
|
<span>
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,13 @@ type GanttTreeListInstance = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gorev listesi kolon sayisina gore acilir; DevExtreme TreeList sabit width verilmezse
|
||||||
|
// kolonlari mevcut genislige esit paylastirip kolon arttikca hepsini okunamaz hale getiriyor.
|
||||||
|
const DEFAULT_TASK_LIST_COLUMN_WIDTH = 100
|
||||||
|
const MIN_TASK_LIST_WIDTH = 300
|
||||||
|
const MAX_TASK_LIST_WIDTH_RATIO = 0.8
|
||||||
|
const TASK_LIST_WIDTH_PADDING = 2
|
||||||
|
|
||||||
const getGanttTreeList = (gantt: GanttInstance) => {
|
const getGanttTreeList = (gantt: GanttInstance) => {
|
||||||
const element = (gantt.element() as HTMLElement).querySelector('.dx-treelist')
|
const element = (gantt.element() as HTMLElement).querySelector('.dx-treelist')
|
||||||
return element
|
return element
|
||||||
|
|
@ -89,7 +96,7 @@ const GanttView = (props: GanttViewProps) => {
|
||||||
const [columnData, setColumnData] = useState<GridColumnData[]>()
|
const [columnData, setColumnData] = useState<GridColumnData[]>()
|
||||||
const gridDto = useListFormGridDto(listFormCode, extGridDto)
|
const gridDto = useListFormGridDto(listFormCode, extGridDto)
|
||||||
const [scaleType, setScaleType] = useState<GanttScaleType>('weeks')
|
const [scaleType, setScaleType] = useState<GanttScaleType>('weeks')
|
||||||
const [taskListWidth, setTaskListWidth] = useState(500)
|
const [taskListWidth, setTaskListWidth] = useState(MIN_TASK_LIST_WIDTH)
|
||||||
const layout = layoutTypes.gantt || 'gantt'
|
const layout = layoutTypes.gantt || 'gantt'
|
||||||
const pendingStateRef = useRef<Record<string, any> | undefined>(undefined)
|
const pendingStateRef = useRef<Record<string, any> | undefined>(undefined)
|
||||||
const loadedStateKeyRef = useRef('')
|
const loadedStateKeyRef = useRef('')
|
||||||
|
|
@ -171,11 +178,47 @@ const GanttView = (props: GanttViewProps) => {
|
||||||
|
|
||||||
return (columnData ?? [])
|
return (columnData ?? [])
|
||||||
.filter((column: any) => column.type !== 'buttons')
|
.filter((column: any) => column.type !== 'buttons')
|
||||||
.map((column: any) =>
|
.map((column: any) => ({
|
||||||
chartFields.has(column.dataField) ? { ...column, visible: false } : column,
|
...column,
|
||||||
)
|
// width baslangic degeridir; alt sinir konmadigi icin fareyle serbestce daraltilabilir.
|
||||||
|
width: column.width ?? DEFAULT_TASK_LIST_COLUMN_WIDTH,
|
||||||
|
...(chartFields.has(column.dataField) ? { visible: false } : {}),
|
||||||
|
}))
|
||||||
}, [columnData, gridDto])
|
}, [columnData, gridDto])
|
||||||
|
|
||||||
|
// Kullanici bolucuyu surukledigi ya da kayitli gorunum yuklendigi anda otomatik
|
||||||
|
// genislik devreden cikar; sonrasinda panel yalnizca fareyle degisir.
|
||||||
|
const taskListWidthLockedRef = useRef(false)
|
||||||
|
|
||||||
|
const clampTaskListWidth = useCallback((width: number) => {
|
||||||
|
const ganttWidth =
|
||||||
|
(gridRef.current?.instance()?.element() as HTMLElement | undefined)?.getBoundingClientRect()
|
||||||
|
.width || window.innerWidth
|
||||||
|
const upperBound = Math.max(
|
||||||
|
MIN_TASK_LIST_WIDTH,
|
||||||
|
Math.round(ganttWidth * MAX_TASK_LIST_WIDTH_RATIO),
|
||||||
|
)
|
||||||
|
|
||||||
|
return Math.min(Math.max(Math.round(width), MIN_TASK_LIST_WIDTH), upperBound)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const defaultTaskListWidth = useMemo(() => {
|
||||||
|
const visibleColumns = ganttColumns.filter((column: any) => column.visible !== false)
|
||||||
|
if (visibleColumns.length === 0) return MIN_TASK_LIST_WIDTH
|
||||||
|
|
||||||
|
return clampTaskListWidth(
|
||||||
|
visibleColumns.reduce(
|
||||||
|
(sum: number, column: any) => sum + (column.width ?? DEFAULT_TASK_LIST_COLUMN_WIDTH),
|
||||||
|
TASK_LIST_WIDTH_PADDING,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}, [clampTaskListWidth, ganttColumns])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (taskListWidthLockedRef.current) return
|
||||||
|
setTaskListWidth(defaultTaskListWidth)
|
||||||
|
}, [defaultTaskListWidth])
|
||||||
|
|
||||||
useEffect(() => setColumnData(memoizedColumns), [memoizedColumns])
|
useEffect(() => setColumnData(memoizedColumns), [memoizedColumns])
|
||||||
useEffect(() => setGanttDataSource(memoizedDataSource), [memoizedDataSource])
|
useEffect(() => setGanttDataSource(memoizedDataSource), [memoizedDataSource])
|
||||||
|
|
||||||
|
|
@ -274,7 +317,10 @@ const GanttView = (props: GanttViewProps) => {
|
||||||
|
|
||||||
treeList.state(treeListState ?? treeState)
|
treeList.state(treeListState ?? treeState)
|
||||||
if (savedScaleType) setScaleType(savedScaleType)
|
if (savedScaleType) setScaleType(savedScaleType)
|
||||||
if (savedTaskListWidth) setTaskListWidth(savedTaskListWidth)
|
if (savedTaskListWidth) {
|
||||||
|
taskListWidthLockedRef.current = true
|
||||||
|
setTaskListWidth(savedTaskListWidth)
|
||||||
|
}
|
||||||
gantt.option({
|
gantt.option({
|
||||||
...(showDependencies !== undefined ? { showDependencies } : {}),
|
...(showDependencies !== undefined ? { showDependencies } : {}),
|
||||||
...(showResources !== undefined ? { showResources } : {}),
|
...(showResources !== undefined ? { showResources } : {}),
|
||||||
|
|
@ -282,8 +328,39 @@ const GanttView = (props: GanttViewProps) => {
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const splitterBoundRef = useRef(false)
|
||||||
|
const columnResizingModeRef = useRef(false)
|
||||||
|
|
||||||
const handleGanttContentReady = useCallback(
|
const handleGanttContentReady = useCallback(
|
||||||
(event: ContentReadyEvent) => {
|
(event: ContentReadyEvent) => {
|
||||||
|
// Gantt ic TreeList'i 'nextColumn' ile olusturuyor; kolon daraltinca fark
|
||||||
|
// komsu kolona gidiyordu.
|
||||||
|
if (!columnResizingModeRef.current) {
|
||||||
|
const treeList = getGanttTreeList(event.component)
|
||||||
|
if (treeList) {
|
||||||
|
columnResizingModeRef.current = true
|
||||||
|
treeList.option('columnResizingMode', 'widget')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DevExtreme, bolucu suruklemesini taskListWidth option'ina yazmadigi icin
|
||||||
|
// kullanicinin elle ayarladigi genislik olaydan yakalanip kilitlenir.
|
||||||
|
if (!splitterBoundRef.current) {
|
||||||
|
const splitter = (event.component.element() as HTMLElement).querySelector(
|
||||||
|
'.dx-splitter-wrapper',
|
||||||
|
)
|
||||||
|
if (splitter) {
|
||||||
|
splitterBoundRef.current = true
|
||||||
|
splitter.addEventListener(
|
||||||
|
'pointerdown',
|
||||||
|
() => {
|
||||||
|
taskListWidthLockedRef.current = true
|
||||||
|
},
|
||||||
|
{ capture: true },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!pendingStateRef.current) return
|
if (!pendingStateRef.current) return
|
||||||
|
|
||||||
const state = pendingStateRef.current
|
const state = pendingStateRef.current
|
||||||
|
|
@ -309,8 +386,9 @@ const GanttView = (props: GanttViewProps) => {
|
||||||
customizationData: '',
|
customizationData: '',
|
||||||
})
|
})
|
||||||
getGanttTreeList(gantt)?.state(null)
|
getGanttTreeList(gantt)?.state(null)
|
||||||
setTaskListWidth(500)
|
taskListWidthLockedRef.current = false
|
||||||
}, [listFormCode, storageKey])
|
setTaskListWidth(defaultTaskListWidth)
|
||||||
|
}, [defaultTaskListWidth, listFormCode, storageKey])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue