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 →
|
||||
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
|
||||
tanımlanır. Controller yazılmaz — ABP otomatik API üretir. Controller sadece ABP
|
||||
konvansiyonunun karşılamadığı durumda (dosya indirme, harici webhook) eklenir.
|
||||
|
|
|
|||
|
|
@ -16,8 +16,11 @@ sorusunda buraya bak.
|
|||
|
||||
**Ortak kurallar**
|
||||
|
||||
- Bütün `*Json` kolonları veritabanında **tek satır JSON string** olarak durur; wizard seed
|
||||
dosyasına yazarken kaçışlanır.
|
||||
- Bütün `*Json` kolonları veritabanında **tek satır JSON string** olarak durur. Wizard seed
|
||||
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.
|
||||
- `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
|
||||
|
|
|
|||
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)
|
||||
├── crud/{EntityName}.json ← tablonun REST uçları
|
||||
├── 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.
|
||||
|
|
@ -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ı |
|
||||
| 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 |
|
||||
| 4 | Wizard | `wizard/{ts}_{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 |
|
||||
| 4 | Wizard | `wizard/{Ad}.json` | Menü, yetki, dil anahtarı ve ListForm'u üretir |
|
||||
| 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
|
||||
göre sıralı** işler. Bir wizard başka bir wizard'ın ürettiği menüye/ListForm'a dayanıyorsa,
|
||||
bağımlı olanın zaman damgası **daha büyük** olmalıdır. Alt form olarak kullanılacak menüsüz
|
||||
ekranı her zaman önce numaralandır.
|
||||
**`Wizard.SeededAt` bir bağımlılık aracıdır.** Dosya adı tarih taşımaz (`{WizardName}.json`);
|
||||
`WizardDataSeeder` `wizard/` klasörünü **`Wizard.SeededAt` damgasına göre sıralı** işler (eşitlikte
|
||||
dosya adı). Bir wizard başka bir wizard'ın ürettiği menüye/ListForm'a dayanıyorsa, bağımlı olanın
|
||||
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
|
||||
|
||||
|
|
@ -102,7 +103,7 @@ Uydurma. Şu üçünü mutlaka kontrol et:
|
|||
| 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`) |
|
||||
| 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 |
|
||||
| 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` |
|
||||
| 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` |
|
||||
| Wizard seed dosyası | `{yyyyMMddHHmmss}_{WizardName}.json` | `20260826101500_Orders.json` |
|
||||
| Wizard seed dosyası | `{WizardName}.json` | `Orders.json` |
|
||||
| Custom component seed | `{Name}.json` | `OrderBoard.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ı)
|
||||
|
||||
Konum: `configs/seeds/{kapsam}/wizard/{yyyyMMddHHmmss}_{WizardName}.json`
|
||||
Okuyan: `WizardDataSeeder` · Yazan: `ListFormWizardAppService`
|
||||
Konum: `configs/seeds/{kapsam}/wizard/{WizardName}.json` (dosya adında tarih yoktur)
|
||||
Okuyan: `WizardDataSeeder` · Yazan: `ListFormWizardAppService` ve `WizardSeedSynchronizer`
|
||||
|
||||
### 4.1 Dosya iskeleti
|
||||
|
||||
```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)
|
||||
"IsCreatedField": true, // tabloda CreatorId var mı (audit)
|
||||
"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
|
||||
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.
|
||||
|
||||
### 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**
|
||||
|
||||
| Alan | Anlam |
|
||||
| --- | --- |
|
||||
| `ComponentKind` | `0` List (varsayılan) · `1` Custom. Dosyadaki ilk alan olmalı. |
|
||||
| `WizardName` | Dosya adının ve export zip adının kaynağı. |
|
||||
| `ListFormCode` | Ekranın kodu; route ve metadata bu koda göre çözülür. |
|
||||
| `WizardName` | Dosya adının (`{WizardName}.json`) ve export zip adının kaynağı. |
|
||||
| `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. |
|
||||
| `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ı. |
|
||||
|
|
@ -600,7 +644,11 @@ Elle dosya yazarken buraya var olan bir kaydı koyma — silme onu da götürür
|
|||
|
||||
| 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. |
|
||||
| `MenuIcon` | React-icons adı (`FcBiohazard`, `FaBox`…). |
|
||||
| `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 |
|
||||
| --- | --- |
|
||||
> `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ı. |
|
||||
| `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. |
|
||||
|
|
@ -624,23 +675,23 @@ edilir.
|
|||
|
||||
| Alan | Anlam |
|
||||
| --- | --- |
|
||||
| `DataSourceCode` | Bağlantı kodu; varsayılan `"Default"`. |
|
||||
| `DataSourceConnectionString` | Yeni bir bağlantı tanımlanıyorsa; aksi hâlde boş. **Asla gerçek kimlik bilgisi yazma.** |
|
||||
| `SelectCommandType` | Bkz. §3. |
|
||||
| `SelectCommand` | Tablo/view adı, fonksiyon çağrısı ya da sorgu metni. |
|
||||
| `KeyFieldName` / `KeyFieldDbSourceType` | Birincil anahtar ve tipi. |
|
||||
| `DataSourceCode` | `[ListForm]` Bağlantı kodu; varsayılan `"Default"`. |
|
||||
| `DataSourceConnectionString` | Yalnızca **yeni** bir bağlantı tanımlanıyorsa doldurulur; boşsa dosyaya hiç yazılmaz. **Asla gerçek kimlik bilgisi yazma.** |
|
||||
| `SelectCommandType` | `[ListForm]` Bkz. §3. |
|
||||
| `SelectCommand` | `[ListForm]` Tablo/view adı, fonksiyon çağrısı ya da sorgu metni. |
|
||||
| `KeyFieldName` / `KeyFieldDbSourceType` | `[ListForm]` Birincil anahtar ve tipi. |
|
||||
|
||||
**Davranış**
|
||||
|
||||
| Alan | Anlam |
|
||||
| --- | --- |
|
||||
| `IsTenant` / `IsBranch` / `IsOrganizationUnit` | Otomatik kırılım filtresi. Tabloda `TenantId` varsa `IsTenant: true` **olmalı**. |
|
||||
| `AllowAdding` / `AllowUpdating` / `AllowDeleting` / `ConfirmDelete` | Toolbar ve satır aksiyonları. |
|
||||
| `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. |
|
||||
| `DefaultLayout` | `"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. |
|
||||
| `IsTenant` / `IsBranch` / `IsOrganizationUnit` | `[ListForm]` Otomatik kırılım filtresi. Tabloda `TenantId` varsa `IsTenant: true` **olmalı**. |
|
||||
| `AllowAdding` / `AllowUpdating` / `AllowDeleting` / `ConfirmDelete` | `[ListForm]` Toolbar ve satır aksiyonları (`EditingOptionDto`). |
|
||||
| `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` | `[ListForm]` `"grid"`, `"card"`, `"pivot"`, `"chart"`, `"tree"`, `"gantt"`, `"scheduler"`, `"todo"`. |
|
||||
| `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 |
|
||||
| --- | --- |
|
||||
|
|
@ -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:
|
||||
|
||||
| Alan | Anlam |
|
||||
|
|
@ -673,16 +729,16 @@ Her `Items` öğesi:
|
|||
| `FieldName` | Veri kolonunun adı (SQL'deki adıyla birebir). |
|
||||
| `CaptionName` | Dil anahtarı ya da düz başlık. |
|
||||
| `TurkishCaption` / `EnglishCaption` | Dil metinleri; ikisi de doldurulur. |
|
||||
| `EditorType` | Bkz. §3. |
|
||||
| `EditorOptions` | DevExtreme editör JSON'u — §6. |
|
||||
| `EditorScript` | Alan davranış script'i — §5. |
|
||||
| `DbSourceType` | Bkz. §3. |
|
||||
| `IsRequired` | Zorunluluk. |
|
||||
| `IncludeInEditingForm` | `false` ise yalnızca grid sütunu olur, forma girmez. |
|
||||
| `ColSpan` | Formda kaç kolon kaplar (grubun `ColCount` değeri içinde). |
|
||||
| `LookupDataSourceType` | `1/2/3` — bkz. §3. `0` = lookup yok. |
|
||||
| `LookupQuery` | `Query` tipinde SQL; `StaticData` tipinde JSON dizisi; `WebService` tipinde URL. |
|
||||
| `ValueExpr` / `DisplayExpr` | Lookup'ın değer ve etiket kolonları. |
|
||||
| `EditorType` | `[Fields]` Bkz. §3. |
|
||||
| `EditorOptions` | `[Fields]` DevExtreme editör JSON'u — §6. |
|
||||
| `EditorScript` | `[Fields]` Alan davranış script'i — §5. |
|
||||
| `DbSourceType` | `[Fields]` Bkz. §3. |
|
||||
| `IsRequired` | `[Fields]` Zorunluluk (`ValidationRuleDto`). |
|
||||
| `IncludeInEditingForm` | `[Fields]` `false` ise yalnızca grid sütunu olur, forma girmez (`EditGroupOrderNo`). |
|
||||
| `ColSpan` | `[Fields]` Formda kaç kolon kaplar (grubun `ColCount` değeri içinde). |
|
||||
| `LookupDataSourceType` | `[Fields]` `1/2/3` — bkz. §3. `0` = lookup yok. |
|
||||
| `LookupQuery` | `[Fields]` `Query` tipinde SQL; `StaticData` tipinde JSON dizisi; `WebService` tipinde URL. |
|
||||
| `ValueExpr` / `DisplayExpr` | `[Fields]` Lookup'ın değer ve etiket kolonları. |
|
||||
|
||||
### 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": {
|
||||
"ComponentKind": 0,
|
||||
"WizardName": "Orders",
|
||||
"SeededAt": "2026-08-26T10:15:00",
|
||||
"ListFormCode": "App.Mrp.Orders",
|
||||
"MenuCode": "App.Mrp.Orders",
|
||||
"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.
|
||||
|
||||
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).
|
||||
|
||||
### 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/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:
|
||||
|
|
@ -3419,8 +3476,8 @@ configs/seeds/host/
|
|||
├── sql/object/Hr_T_AdvanceRequest.sql ← tablo (+ PostgreSQL hedefse postgres/object/…)
|
||||
├── crud/Hr_T_AdvanceRequest.json ← yalnızca Custom Component yolunda gerekir
|
||||
├── custom/AdvanceRequestForm.json ← yalnızca serbest yerleşim isteniyorsa
|
||||
├── wizard/20260826101000_AdvanceRequestApprovals.json ← (varsa) menüsüz alt ekran, önce
|
||||
└── wizard/20260826101500_AdvanceRequests.json ← ana ekran + menü + yetki + dil + onay akışı
|
||||
├── wizard/AdvanceRequestApprovals.json ← (varsa) menüsüz alt ekran, küçük SeededAt
|
||||
└── wizard/AdvanceRequests.json ← ana ekran + menü + yetki + dil + onay akışı
|
||||
```
|
||||
|
||||
**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:
|
||||
|
||||
- [ ] 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
|
||||
üretildi.
|
||||
- [ ] `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`)
|
||||
6. **Dil anahtarları** (menü, başlık, açıklama; EN + TR)
|
||||
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ı
|
||||
|
||||
**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
|
||||
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
|
||||
dosyasında ilk alandır. Varsayılan `List` olduğu için bu alanı taşımayan eski seed dosyaları
|
||||
eskisi gibi çalışır.
|
||||
|
|
@ -1165,7 +1175,7 @@ geri yüklenir. Kapsam klasörü CDN düzeniyle aynıdır ve `SeedPathResolver`
|
|||
```
|
||||
configs/seeds/
|
||||
├── host/ # host kapsamı
|
||||
│ ├── wizard/ # {zaman damgası}_{Ad}.json → WizardDataSeeder
|
||||
│ ├── wizard/ # {Ad}.json → WizardDataSeeder
|
||||
│ ├── custom/ # {ComponentName}.json → CustomComponentDataSeeder
|
||||
│ ├── crud/ # {EntityName}.json → CrudDataSeeder
|
||||
│ ├── 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.
|
||||
> 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); }
|
||||
}
|
||||
|
||||
/// <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); }
|
||||
}
|
||||
|
||||
/// <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.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Sozsoft.Languages.Languages;
|
||||
using Sozsoft.Platform.Enums;
|
||||
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -23,6 +38,13 @@ public class ListFormWizardDto
|
|||
public string EditFileName { 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 MenuCode { get; set; }
|
||||
public int MenuOrder { get; set; }
|
||||
|
|
@ -107,4 +129,450 @@ public class ListFormWizardDto
|
|||
public List<WizardColumnGroupInputDto> Groups { get; set; } = new();
|
||||
public List<SubFormDto> SubForms { 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 Volo.Abp.Application.Dtos;
|
||||
|
||||
namespace Sozsoft.Platform.ListForms;
|
||||
|
||||
|
|
@ -12,6 +17,15 @@ public class WizardSeedFileDto
|
|||
{
|
||||
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>
|
||||
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.
|
||||
/// </summary>
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -21,12 +21,16 @@ public class ListFormFieldsAppService : CrudAppService<
|
|||
{
|
||||
public IRepository<ListForm, Guid> RepoListForm { get; }
|
||||
|
||||
private readonly IWizardSeedSynchronizer wizardSeedSynchronizer;
|
||||
|
||||
public ListFormFieldsAppService(
|
||||
IRepository<ListFormField, Guid> _repository,
|
||||
IRepository<ListForm, Guid> _repoListForm
|
||||
IRepository<ListForm, Guid> _repoListForm,
|
||||
IWizardSeedSynchronizer _wizardSeedSynchronizer
|
||||
) : base(_repository)
|
||||
{
|
||||
RepoListForm = _repoListForm;
|
||||
wizardSeedSynchronizer = _wizardSeedSynchronizer;
|
||||
|
||||
CreatePolicyName = $"{AppCodes.Listforms.ListformField}.Create";
|
||||
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)
|
||||
|
|
@ -214,9 +223,20 @@ public class ListFormFieldsAppService : CrudAppService<
|
|||
|
||||
await Repository.UpdateAsync(item, autoSave: true);
|
||||
|
||||
await wizardSeedSynchronizer.SyncAsync(item.ListFormCode);
|
||||
|
||||
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")]
|
||||
public async Task<CopyListFormFieldColumnDto> CopyField(CopyListFormFieldColumnDto input)
|
||||
{
|
||||
|
|
@ -324,6 +344,8 @@ public class ListFormFieldsAppService : CrudAppService<
|
|||
|
||||
await Repository.InsertAsync(field);
|
||||
|
||||
await wizardSeedSynchronizer.SyncAsync(field.ListFormCode);
|
||||
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,12 +20,15 @@ namespace Sozsoft.Platform.ListForms.Administration;
|
|||
public class ListFormJsonRowAppService : PlatformAppService
|
||||
{
|
||||
private readonly IRepository<ListForm, Guid> repository;
|
||||
private readonly IWizardSeedSynchronizer wizardSeedSynchronizer;
|
||||
|
||||
public ListFormJsonRowAppService(
|
||||
IRepository<ListForm, Guid> _repository
|
||||
IRepository<ListForm, Guid> _repository,
|
||||
IWizardSeedSynchronizer _wizardSeedSynchronizer
|
||||
) : base()
|
||||
{
|
||||
repository = _repository;
|
||||
wizardSeedSynchronizer = _wizardSeedSynchronizer;
|
||||
}
|
||||
|
||||
public async Task<dynamic> GetAllAsync(Guid id, string field)
|
||||
|
|
@ -183,6 +186,9 @@ public class ListFormJsonRowAppService : PlatformAppService
|
|||
}
|
||||
|
||||
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)
|
||||
|
|
@ -237,6 +243,9 @@ public class ListFormJsonRowAppService : PlatformAppService
|
|||
}
|
||||
|
||||
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)
|
||||
|
|
@ -291,6 +300,9 @@ public class ListFormJsonRowAppService : PlatformAppService
|
|||
}
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -21,12 +21,15 @@ public class ListFormsAppService : CrudAppService<
|
|||
GridOptionsEditDto>, IListFormsAppService
|
||||
{
|
||||
private readonly IListFormAuthorizationManager authManager;
|
||||
private readonly IWizardSeedSynchronizer wizardSeedSynchronizer;
|
||||
|
||||
public ListFormsAppService(
|
||||
IRepository<ListForm, Guid> _repository,
|
||||
IListFormAuthorizationManager _authManager) : base(_repository)
|
||||
IListFormAuthorizationManager _authManager,
|
||||
IWizardSeedSynchronizer _wizardSeedSynchronizer) : base(_repository)
|
||||
{
|
||||
authManager = _authManager;
|
||||
wizardSeedSynchronizer = _wizardSeedSynchronizer;
|
||||
|
||||
CreatePolicyName = $"{AppCodes.Listforms.Listform}.Create";
|
||||
UpdatePolicyName = $"{AppCodes.Listforms.Listform}.Update";
|
||||
|
|
@ -239,6 +242,9 @@ public class ListFormsAppService : CrudAppService<
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,6 @@ public class ListFormWizardAppService(
|
|||
private readonly string cultureNameDefault = PlatformConsts.DefaultLanguage;
|
||||
|
||||
private static readonly JsonSerializerOptions SeedJsonReadOptions = new() { PropertyNameCaseInsensitive = true };
|
||||
private static readonly JsonSerializerOptions SeedJsonWriteOptions = new() { WriteIndented = true };
|
||||
|
||||
[UnitOfWork]
|
||||
public async Task Create(ListFormWizardDto input)
|
||||
|
|
@ -92,18 +91,18 @@ public class ListFormWizardAppService(
|
|||
: AppCodes.Listforms.Wizards.Create);
|
||||
|
||||
if (isEdit)
|
||||
{
|
||||
// Ilk deploy zamani wizard'in kimligidir; duzenleme onu degistirmez.
|
||||
input.SeededAt = await ReadSeededAtAsync(editFileName!);
|
||||
await DeleteFileInternalAsync(editFileName!);
|
||||
}
|
||||
|
||||
var wizardName = input.WizardName?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(wizardName))
|
||||
throw new UserFriendlyException("Wizard adı zorunludur.");
|
||||
|
||||
var code = string.IsNullOrWhiteSpace(input.MenuCode)
|
||||
? WizardConsts.WizardKey(wizardName)
|
||||
: input.MenuCode.Trim();
|
||||
var listFormCode = string.IsNullOrWhiteSpace(input.ListFormCode)
|
||||
? code
|
||||
: input.ListFormCode.Trim();
|
||||
var code = input.ResolveMenuCode();
|
||||
var listFormCode = input.ResolveListFormCode();
|
||||
// Aşağıdaki tüm kayıtlar normalize edilmiş kodlara göre üretilir.
|
||||
input.ListFormCode = listFormCode;
|
||||
input.MenuCode = code;
|
||||
|
|
@ -295,7 +294,7 @@ public class ListFormWizardAppService(
|
|||
if (isCustomComponent)
|
||||
{
|
||||
await _languageTextAppService.ClearRedisCacheAsync();
|
||||
await SaveWizardSeedFileAsync(input, false, false, inserted);
|
||||
await SaveWizardSeedFileAsync(input, null, [], false, false, inserted);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -318,21 +317,7 @@ public class ListFormWizardAppService(
|
|||
inserted.DataSourceCodes.Add(input.DataSourceCode);
|
||||
}
|
||||
|
||||
// Build EditingFormJson from wizard groups (sadece editing form alanı olan gruplar)
|
||||
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
|
||||
//ListForm ve alanlari - varsa sil, yeniden uret
|
||||
var existingListForm = await repoListForm.FirstOrDefaultAsync(a => a.ListFormCode == input.ListFormCode);
|
||||
if (existingListForm != null)
|
||||
{
|
||||
|
|
@ -354,115 +339,27 @@ public class ListFormWizardAppService(
|
|||
var tableColumns = await GetTableColumnNamesAsync(input.DataSourceCode, input.SelectCommandType, input.SelectCommand);
|
||||
var isDeleted = tableColumns.Contains("IsDeleted");
|
||||
var isCreated = tableColumns.Contains("CreatorId");
|
||||
input.WorkflowDto ??= new WorkflowDto();
|
||||
input.WorkflowDto.Criteria ??= [];
|
||||
EnsureUniqueWorkflowCriteriaTitles(input.WorkflowDto.Criteria);
|
||||
NormalizeWorkflowCriteriaIds(listFormCode, input.WorkflowDto.Criteria);
|
||||
|
||||
// Ekranin sozlesmesi tek fabrikadan uretilir; seed dosyasina yazilan da veritabanina
|
||||
// yazilan da ayni nesnedir.
|
||||
var listFormDto = input.BuildListForm(isDeleted, isCreated);
|
||||
var fieldDtos = input.BuildFields();
|
||||
var workflow = input.WorkflowDto;
|
||||
|
||||
await repoListForm.InsertAsync(new ListForm
|
||||
{
|
||||
ListFormType = ListFormTypeEnum.List,
|
||||
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);
|
||||
var listForm = new ListForm();
|
||||
listFormDto.ApplyTo(listForm);
|
||||
await repoListForm.InsertAsync(listForm, autoSave: true);
|
||||
|
||||
// ListFormField - each item in each group becomes a visible field record
|
||||
var fieldOrder = 0;
|
||||
var editGroupOrder = 0;
|
||||
foreach (var group in input.Groups)
|
||||
foreach (var fieldDto in fieldDtos)
|
||||
{
|
||||
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,
|
||||
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);
|
||||
var field = new ListFormField();
|
||||
fieldDto.ApplyTo(field);
|
||||
await repoListFormField.InsertAsync(field, 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)
|
||||
|
|
@ -496,153 +393,48 @@ public class ListFormWizardAppService(
|
|||
await _languageTextAppService.ClearRedisCacheAsync();
|
||||
|
||||
// Wizard konfigürasyonunu seed dosyasına kaydet
|
||||
await SaveWizardSeedFileAsync(input, 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;
|
||||
await SaveWizardSeedFileAsync(input, listFormDto, fieldDtos, isDeleted, isCreated, inserted);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// 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>
|
||||
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
|
||||
{
|
||||
var outputPath = ResolveWizardSeedOutputPath();
|
||||
Directory.CreateDirectory(outputPath);
|
||||
|
||||
// Duzenleme bilgisi yalnizca istege aittir; seed dosyasinda yeri yoktur.
|
||||
input.EditFileName = null;
|
||||
// Duzenlemede ilk deploy zamani korunur; seeder dosyalari bu damgaya gore siralar.
|
||||
if (input.SeededAt == default)
|
||||
input.SeededAt = Clock.Now;
|
||||
|
||||
var seedData = new WizardSeedFileDto
|
||||
{
|
||||
Wizard = input,
|
||||
ListForm = listForm,
|
||||
Fields = fields ?? [],
|
||||
IsDeletedField = isDeletedField,
|
||||
IsCreatedField = isCreatedField,
|
||||
InsertedRecords = inserted
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(seedData, SeedJsonWriteOptions);
|
||||
// Dosya adı kullanıcı girdisinden türetildiği için geçersiz karakterler ayıklanır.
|
||||
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);
|
||||
var filePath = Path.Combine(outputPath, ResolveWizardSeedFileName(input.WizardName));
|
||||
await File.WriteAllTextAsync(filePath, seedData.ToSeedJson());
|
||||
|
||||
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>
|
||||
/// 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>)
|
||||
|
|
@ -724,7 +548,7 @@ public class ListFormWizardAppService(
|
|||
try
|
||||
{
|
||||
var json = await File.ReadAllTextAsync(file);
|
||||
var seed = JsonSerializer.Deserialize<WizardSeedFileDto>(json, SeedJsonReadOptions);
|
||||
var seed = WizardSeedFileDto.FromSeedJson(json);
|
||||
result.Add(new WizardFileInfoDto
|
||||
{
|
||||
FileName = fileName,
|
||||
|
|
@ -735,7 +559,7 @@ public class ListFormWizardAppService(
|
|||
ComponentKind = seed?.Wizard?.ComponentKind ?? WizardComponentKindEnum.List,
|
||||
CustomComponentName = seed?.Wizard?.CustomComponentName ?? 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 &&
|
||||
(seed.InsertedRecords.LanguageKeys.Count > 0 ||
|
||||
seed.InsertedRecords.PermissionGroupNames.Count > 0 ||
|
||||
|
|
@ -759,7 +583,7 @@ public class ListFormWizardAppService(
|
|||
var filePath = ResolveWizardSeedFilePath(fileName);
|
||||
|
||||
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ı.");
|
||||
}
|
||||
|
||||
|
|
@ -779,7 +603,7 @@ public class ListFormWizardAppService(
|
|||
var filePath = ResolveWizardSeedFilePath(fileName);
|
||||
|
||||
var json = await File.ReadAllTextAsync(filePath);
|
||||
var seed = JsonSerializer.Deserialize<WizardSeedFileDto>(json, SeedJsonReadOptions);
|
||||
var seed = WizardSeedFileDto.FromSeedJson(json);
|
||||
|
||||
if (seed != null)
|
||||
await DeleteWizardDataAsync(seed);
|
||||
|
|
@ -992,7 +816,7 @@ public class ListFormWizardAppService(
|
|||
{
|
||||
var filePath = ResolveWizardSeedFilePath(fileName);
|
||||
var json = await File.ReadAllTextAsync(filePath);
|
||||
var seed = JsonSerializer.Deserialize<WizardSeedFileDto>(json, SeedJsonReadOptions)
|
||||
var seed = WizardSeedFileDto.FromSeedJson(json)
|
||||
?? throw new UserFriendlyException("Dosya okunamadı.");
|
||||
|
||||
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",
|
||||
"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",
|
||||
"key": "App.SqlQueryManager.CrudEndpointsInactiveNote",
|
||||
|
|
|
|||
|
|
@ -84,30 +84,44 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
return;
|
||||
}
|
||||
|
||||
var jsonFiles = Directory.GetFiles(wizardDataPath, "*.json").OrderBy(f => Path.GetFileName(f)).ToArray();
|
||||
var jsonFiles = Directory.GetFiles(wizardDataPath, "*.json");
|
||||
if (jsonFiles.Length == 0)
|
||||
{
|
||||
_logger.LogInformation("No JSON files found in Seeds/{Scope}/wizard directory, skipping.", scopeFolderName);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation("WizardDataSeeder started. {Count} files to be processed.", jsonFiles.Length);
|
||||
|
||||
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
||||
|
||||
// Dosya adi tarih tasimadigi icin uygulama sirasi dosyanin kendi damgasindan gelir;
|
||||
// birbirine bagli wizard'lar (once Customers, sonra Tasks) uretildikleri sirayla uygulanir.
|
||||
var seedFiles = new List<(string FilePath, WizardSeedFileDto Seed)>();
|
||||
foreach (var filePath in jsonFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = await File.ReadAllTextAsync(filePath);
|
||||
var seedFile = JsonSerializer.Deserialize<WizardSeedFileDto>(json, options);
|
||||
|
||||
if (seedFile?.Wizard == null)
|
||||
var seed = WizardSeedFileDto.FromSeedJson(await File.ReadAllTextAsync(filePath));
|
||||
if (seed?.Wizard == null)
|
||||
{
|
||||
_logger.LogWarning("Invalid file skipped: {FilePath}", filePath);
|
||||
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();
|
||||
if (string.IsNullOrWhiteSpace(wizardName))
|
||||
{
|
||||
|
|
@ -115,7 +129,12 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
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);
|
||||
|
||||
|
|
@ -123,10 +142,10 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
// idempotency menü kaydı üzerinden değerlendirilir.
|
||||
// Menusuz Custom wizard'da ne ListForm ne de menu kaydi olustugu icin
|
||||
// idempotency okuma permission'i uzerinden degerlendirilir.
|
||||
var alreadySeeded = seedFile.Wizard.ComponentKind == WizardComponentKindEnum.Custom
|
||||
var alreadySeeded = isCustomComponent
|
||||
? seedFile.Wizard.CreateMenu
|
||||
? await _repoMenu.AnyAsync(a => a.Code == ResolveMenuCode(seedFile.Wizard))
|
||||
: await _repoPerm.AnyAsync(a => a.Name == ResolveMenuCode(seedFile.Wizard))
|
||||
? await _repoMenu.AnyAsync(a => a.Code == seedFile.Wizard.ResolveMenuCode())
|
||||
: await _repoPerm.AnyAsync(a => a.Name == seedFile.Wizard.ResolveMenuCode())
|
||||
: await _repoListForm.AnyAsync(a => a.ListFormCode == seedFile.Wizard.ListFormCode);
|
||||
|
||||
if (alreadySeeded)
|
||||
|
|
@ -149,23 +168,10 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
private async Task ApplyWizardSeedAsync(WizardSeedFileDto seedFile)
|
||||
{
|
||||
var input = seedFile.Wizard;
|
||||
var isDeleted = seedFile.IsDeletedField;
|
||||
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;
|
||||
input.Groups ??= [];
|
||||
|
||||
var wizardName = input.WizardName.Trim();
|
||||
var code = string.IsNullOrWhiteSpace(input.MenuCode)
|
||||
? WizardConsts.WizardKey(wizardName)
|
||||
: input.MenuCode.Trim();
|
||||
var listFormCode = string.IsNullOrWhiteSpace(input.ListFormCode)
|
||||
? code
|
||||
: input.ListFormCode.Trim();
|
||||
NormalizeWorkflowCriteriaIds(listFormCode, workflow.Criteria);
|
||||
var code = input.ResolveMenuCode();
|
||||
var listFormCode = input.ResolveListFormCode();
|
||||
var titleLangKey = $"{listFormCode}.Title";
|
||||
var nameLangKey = code;
|
||||
var descLangKey = $"{listFormCode}.Desc";
|
||||
|
|
@ -344,20 +350,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
}, autoSave: true);
|
||||
}
|
||||
|
||||
// EditingFormJson
|
||||
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
|
||||
// ListForm ve alanlari - varsa sil, seed dosyasindaki sozlesmeden yeniden kur
|
||||
var existingListForm = await _repoListForm.FirstOrDefaultAsync(a => a.ListFormCode == input.ListFormCode);
|
||||
if (existingListForm != null)
|
||||
{
|
||||
|
|
@ -376,170 +369,26 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
await _repoListFormWorkflow.DeleteManyAsync(existingWorkflowCriteria, autoSave: true);
|
||||
}
|
||||
|
||||
// EditingForm'da required olan alanları tespit et, ValidationRuleJson eklemek için
|
||||
var editingFormFieldNames = input.Groups
|
||||
.SelectMany(g => g.Items)
|
||||
.Where(i => i.IncludeInEditingForm && i.FieldName != input.KeyFieldName)
|
||||
.Select(i => new { FieldName = i.FieldName, Isrequired = i.IsRequired })
|
||||
.ToList();
|
||||
// Ekran, seed dosyasinin tasidigi ListForm sozlesmesinden kurulur; boylece wizard
|
||||
// sonrasinda tasarimci uzerinde yapilan degisiklikler de geri gelir.
|
||||
var listForm = new ListForm();
|
||||
seedFile.ListForm.ApplyTo(listForm);
|
||||
await _repoListForm.InsertAsync(listForm, autoSave: true);
|
||||
|
||||
// Heigth ve Width hesaplama
|
||||
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
|
||||
foreach (var fieldDto in seedFile.Fields)
|
||||
{
|
||||
ListFormType = ListFormTypeEnum.List,
|
||||
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 = 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);
|
||||
}
|
||||
var field = new ListFormField();
|
||||
fieldDto.ApplyTo(field);
|
||||
await _repoListFormField.InsertAsync(field, autoSave: true);
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
|
|
@ -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>
|
||||
/// Custom yolunda menunun acacagi rotayi normalize eder. Rota MenuUrl uzerinden
|
||||
/// tasinir; bos ise menu hicbir yere gitmeyecegi icin seed dosyasi hatali kabul edilir.
|
||||
|
|
@ -647,119 +485,6 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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.Data;
|
||||
using Sozsoft.Platform.Enums;
|
||||
using Sozsoft.Platform.ListForms;
|
||||
using Volo.Abp.Domain.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 CultureName { get; set; } // Bu tanim hangi dil icin "en", "tr"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Data;
|
||||
using Sozsoft.Platform.ListForms;
|
||||
using Volo.Abp.Domain.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 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.
|
||||
İ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.
|
||||
- **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)
|
||||
`permission.constant.ts` + `LanguagesData.json`.
|
||||
- **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 {
|
||||
GanttOptionDto,
|
||||
GridOptionsEditDto,
|
||||
SchedulerOptionDto,
|
||||
SubFormDto,
|
||||
TodoOptionDto,
|
||||
|
|
@ -43,6 +45,11 @@ export interface ListFormWizardDto {
|
|||
editFileName?: 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
|
||||
menuCode: string
|
||||
menuOrder: number
|
||||
|
|
@ -168,6 +175,10 @@ export interface WizardSeedFileWizardDto extends Omit<ListFormWizardDto, 'groups
|
|||
|
||||
export interface WizardSeedFileDto {
|
||||
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
|
||||
isCreatedField: boolean
|
||||
insertedRecords: WizardInsertedRecordsDto
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import {
|
|||
ColumnFormatEditDto,
|
||||
CopyListFormFieldColumnDto,
|
||||
} from '@/proxy/admin/list-form-field/models'
|
||||
import { LanguageInfo } from '@/proxy/config/models'
|
||||
import { SelectBoxOption } from '@/types/shared'
|
||||
import { useStoreState } from '@/store'
|
||||
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 FormFieldEdit from './FormFieldEdit'
|
||||
import { dbSourceTypeOptions, sqlDataTypeToDbType } from '../options'
|
||||
import { IdentityRoleDto, IdentityUserDto } from '@/proxy/admin/models'
|
||||
import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
||||
import type { DatabaseColumnDto } from '@/proxy/sql-query-manager/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 CreatableSelect from 'react-select/creatable'
|
||||
import {
|
||||
FaArrowDown,
|
||||
FaArrowLeft,
|
||||
FaArrowRight,
|
||||
FaArrowUp,
|
||||
FaChevronDown,
|
||||
FaChevronRight,
|
||||
FaEdit,
|
||||
|
|
@ -47,6 +49,14 @@ interface TreeNodeProps {
|
|||
saving: boolean
|
||||
onStartEdit: (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({
|
||||
|
|
@ -59,6 +69,9 @@ function TreeNode({
|
|||
saving,
|
||||
onStartEdit,
|
||||
onDelete,
|
||||
siblings,
|
||||
index,
|
||||
onMove,
|
||||
}: TreeNodeProps) {
|
||||
const hasChildren = node.children.length > 0
|
||||
const isExpanded = expanded.has(node.code)
|
||||
|
|
@ -114,6 +127,34 @@ function TreeNode({
|
|||
</span>
|
||||
|
||||
<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
|
||||
type="button"
|
||||
disabled={saving}
|
||||
|
|
@ -146,7 +187,7 @@ function TreeNode({
|
|||
</div>
|
||||
|
||||
{isExpanded &&
|
||||
node.children.map((child) => (
|
||||
node.children.map((child, childIndex) => (
|
||||
<TreeNode
|
||||
key={child.code}
|
||||
node={child as MenuTreeNode & { id?: string }}
|
||||
|
|
@ -158,6 +199,9 @@ function TreeNode({
|
|||
saving={saving}
|
||||
onStartEdit={onStartEdit}
|
||||
onDelete={onDelete}
|
||||
siblings={node.children as (MenuTreeNode & { id?: string })[]}
|
||||
index={childIndex}
|
||||
onMove={onMove}
|
||||
/>
|
||||
))}
|
||||
</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.
|
||||
const enrichedNodes = useMemo(() => {
|
||||
const idByCode = new Map(rawItems.map((item) => [item.code, item.id]))
|
||||
|
|
@ -254,6 +342,7 @@ function MenuTreeInline({
|
|||
saving,
|
||||
onStartEdit: onEditMenu,
|
||||
onDelete: handleDelete,
|
||||
onMove: handleMove,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -269,13 +358,15 @@ function MenuTreeInline({
|
|||
{translate('::App.WizardStep1.NoMenusAvailable')}
|
||||
</div>
|
||||
) : (
|
||||
enrichedNodes.map((node) => (
|
||||
enrichedNodes.map((node, index) => (
|
||||
<TreeNode
|
||||
key={node.code}
|
||||
node={node}
|
||||
depth={0}
|
||||
selectedCode={value}
|
||||
onSelect={onChange}
|
||||
siblings={enrichedNodes}
|
||||
index={index}
|
||||
{...sharedNodeProps}
|
||||
/>
|
||||
))
|
||||
|
|
|
|||
|
|
@ -24,11 +24,14 @@ import {
|
|||
FaTasks,
|
||||
FaChartBar,
|
||||
FaRegCalendarAlt,
|
||||
FaToggleOn,
|
||||
FaToggleOff,
|
||||
} from 'react-icons/fa'
|
||||
import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
||||
import { getMenus } from '@/services/menu.service'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { CascadeBehavior, SqlTableRelation, RelationshipType } from '@/proxy/developerKit/models'
|
||||
import type { CrudEndpoint } from '@/proxy/developerKit/models'
|
||||
import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
|
||||
import { MenuItem } from '@/proxy/menus/menu'
|
||||
import {
|
||||
|
|
@ -45,6 +48,7 @@ import {
|
|||
CRUD_ENDPOINT_PERMISSION,
|
||||
CRUD_OPERATION_TYPES,
|
||||
type CrudOperationType,
|
||||
METHOD_COLOR,
|
||||
getErrorMessage,
|
||||
getOperationMeta,
|
||||
toEntityName,
|
||||
|
|
@ -1333,6 +1337,9 @@ const SqlTableDesignerDialog = ({
|
|||
const [crudOperations, setCrudOperations] = useState<CrudOperationType[]>([
|
||||
...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 [originalColumns, setOriginalColumns] = useState<ColumnDefinition[]>([])
|
||||
const [colsLoading, setColsLoading] = useState(false)
|
||||
|
|
@ -1418,6 +1425,24 @@ const SqlTableDesignerDialog = ({
|
|||
.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
|
||||
if (initialTableData && dataSource) {
|
||||
setColsLoading(true)
|
||||
|
|
@ -1541,7 +1566,7 @@ const SqlTableDesignerDialog = ({
|
|||
.catch(() => {})
|
||||
.finally(() => setIndexesLoading(false))
|
||||
}
|
||||
}, [isOpen, dataSource, initialTableData])
|
||||
}, [isOpen, dataSource, initialTableData, canManageCrudEndpoints])
|
||||
|
||||
const generatedSql = useMemo(
|
||||
() =>
|
||||
|
|
@ -2126,6 +2151,7 @@ const SqlTableDesignerDialog = ({
|
|||
setTemplatesOpen(false)
|
||||
setGenerateCrud(false)
|
||||
setCrudOperations([...CRUD_OPERATION_TYPES])
|
||||
setExistingCrudEndpoints([])
|
||||
onClose()
|
||||
}
|
||||
|
||||
|
|
@ -3343,6 +3369,43 @@ const SqlTableDesignerDialog = ({
|
|||
|
||||
const renderCrudEndpoints = () => (
|
||||
<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">
|
||||
<Checkbox checked={generateCrud} onChange={(checked) => setGenerateCrud(checked)} />
|
||||
<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 element = (gantt.element() as HTMLElement).querySelector('.dx-treelist')
|
||||
return element
|
||||
|
|
@ -89,7 +96,7 @@ const GanttView = (props: GanttViewProps) => {
|
|||
const [columnData, setColumnData] = useState<GridColumnData[]>()
|
||||
const gridDto = useListFormGridDto(listFormCode, extGridDto)
|
||||
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 pendingStateRef = useRef<Record<string, any> | undefined>(undefined)
|
||||
const loadedStateKeyRef = useRef('')
|
||||
|
|
@ -171,11 +178,47 @@ const GanttView = (props: GanttViewProps) => {
|
|||
|
||||
return (columnData ?? [])
|
||||
.filter((column: any) => column.type !== 'buttons')
|
||||
.map((column: any) =>
|
||||
chartFields.has(column.dataField) ? { ...column, visible: false } : column,
|
||||
)
|
||||
.map((column: any) => ({
|
||||
...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])
|
||||
|
||||
// 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(() => setGanttDataSource(memoizedDataSource), [memoizedDataSource])
|
||||
|
||||
|
|
@ -274,7 +317,10 @@ const GanttView = (props: GanttViewProps) => {
|
|||
|
||||
treeList.state(treeListState ?? treeState)
|
||||
if (savedScaleType) setScaleType(savedScaleType)
|
||||
if (savedTaskListWidth) setTaskListWidth(savedTaskListWidth)
|
||||
if (savedTaskListWidth) {
|
||||
taskListWidthLockedRef.current = true
|
||||
setTaskListWidth(savedTaskListWidth)
|
||||
}
|
||||
gantt.option({
|
||||
...(showDependencies !== undefined ? { showDependencies } : {}),
|
||||
...(showResources !== undefined ? { showResources } : {}),
|
||||
|
|
@ -282,8 +328,39 @@ const GanttView = (props: GanttViewProps) => {
|
|||
})
|
||||
}, [])
|
||||
|
||||
const splitterBoundRef = useRef(false)
|
||||
const columnResizingModeRef = useRef(false)
|
||||
|
||||
const handleGanttContentReady = useCallback(
|
||||
(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
|
||||
|
||||
const state = pendingStateRef.current
|
||||
|
|
@ -309,8 +386,9 @@ const GanttView = (props: GanttViewProps) => {
|
|||
customizationData: '',
|
||||
})
|
||||
getGanttTreeList(gantt)?.state(null)
|
||||
setTaskListWidth(500)
|
||||
}, [listFormCode, storageKey])
|
||||
taskListWidthLockedRef.current = false
|
||||
setTaskListWidth(defaultTaskListWidth)
|
||||
}, [defaultTaskListWidth, listFormCode, storageKey])
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
|
|
|
|||
Loading…
Reference in a new issue