Devexpress Grid kısmına Rating komponenti eklendi

This commit is contained in:
Sedat ÖZTÜRK 2026-10-09 22:40:51 +03:00
parent f341730b4a
commit f44308756c
33 changed files with 496 additions and 238 deletions

View file

@ -370,6 +370,7 @@ dörtlüyle üretilir (§9.8), görseldeki görünüm `DefaultLayout` olur.
| Renk kutucuğu | `dxColorBox` | | | Renk kutucuğu | `dxColorBox` | |
| Kalın/italik araç çubuklu alan | `dxHtmlEditor` | `toolbar.items` | | Kalın/italik araç çubuklu alan | `dxHtmlEditor` | `toolbar.items` |
| Kaydırıcı | `dxSlider` / `dxRangeSlider` | `min`, `max` | | Kaydırıcı | `dxSlider` / `dxRangeSlider` | `min`, `max` |
| Yıldızlı puan (1–5) | `dxRating`, kolon `int` | `count`, `allowClear` |
| Fotoğraf / görsel alanı (seçilen, çekilen ya da başka kayıttan kopyalanan) | `dxImageUpload`, kolon `nvarchar(MAX)` (base64); salt okunur gösterilecekse `EditorOptions: {"readOnly": true}` — `dxImageViewer` değil | `accept`, `maxFileSize`, `capture` | | Fotoğraf / görsel alanı (seçilen, çekilen ya da başka kayıttan kopyalanan) | `dxImageUpload`, kolon `nvarchar(MAX)` (base64); salt okunur gösterilecekse `EditorOptions: {"readOnly": true}` — `dxImageViewer` değil | `accept`, `maxFileSize`, `capture` |
| Gri, tıklanamaz alan | ilgili editör + `EditorOptions: {"readOnly": true}` | | | Gri, tıklanamaz alan | ilgili editör + `EditorOptions: {"readOnly": true}` | |
| Kırmızı yıldızlı etiket | `IsRequired: true` | | | Kırmızı yıldızlı etiket | `IsRequired: true` | |
@ -1072,12 +1073,12 @@ yazılır:
| 25 | Xml | `xml` | | 25 | Xml | `xml` |
| 27 | DateTimeOffset | `datetimeoffset` | | 27 | DateTimeOffset | `datetimeoffset` |
**Editör tipleri (`EditorType2`)** — 21 tip, alana **metin** olarak yazılır. DevExtreme'den **Editör tipleri (`EditorType2`)** — 22 tip, alana **metin** olarak yazılır. DevExtreme'den
gelenler: `dxAutocomplete`, `dxCalendar`, `dxCheckBox`, `dxColorBox`, `dxDateBox`, gelenler: `dxAutocomplete`, `dxCalendar`, `dxCheckBox`, `dxColorBox`, `dxDateBox`,
`dxDateRangeBox`, `dxDropDownBox`, `dxHtmlEditor`, `dxLookup`, `dxNumberBox`, `dxRadioGroup`, `dxDateRangeBox`, `dxDropDownBox`, `dxHtmlEditor`, `dxLookup`, `dxNumberBox`, `dxRadioGroup`,
`dxRangeSlider`, `dxSelectBox`, `dxSlider`, `dxSwitch`, `dxTagBox`, `dxTextArea`, `dxTextBox`. `dxRangeSlider`, `dxSelectBox`, `dxSlider`, `dxSwitch`, `dxTagBox`, `dxTextArea`, `dxTextBox`.
Platformun kendi editörleri (`PlatformEditorTypes`): `dxGridBox`, `dxImageViewer`, Platformun kendi editörleri (`PlatformEditorTypes`): `dxGridBox`, `dxImageViewer`,
`dxImageUpload` (+ `dxTagBox` platform tarafında da özel işlenir). `dxImageUpload`, `dxRating` (+ `dxTagBox` platform tarafında da özel işlenir).
> **Tuzak:** `PlatformConsts.EditorTypes.dxLookup` sabiti bozuk bir metin taşır > **Tuzak:** `PlatformConsts.EditorTypes.dxLookup` sabiti bozuk bir metin taşır
> (`"dxHtmlEdidxLookuptor"`). Hiçbir yerde kullanılmadığı için etkisi yoktur, ama C# seeder > (`"dxHtmlEdidxLookuptor"`). Hiçbir yerde kullanılmadığı için etkisi yoktur, ama C# seeder
@ -1412,6 +1413,7 @@ anahtarları listeler; ortak anahtarlar (§4.3.3) her editörde geçerlidir.
| `dxColorBox` | Renk | String (16) | `editAlphaChannel`, `keyStep` | | `dxColorBox` | Renk | String (16) | `editAlphaChannel`, `keyStep` |
| `dxSlider` | Tek değerli kaydırıcı | Sayı | `min`, `max`, `tooltip.showMode` | | `dxSlider` | Tek değerli kaydırıcı | Sayı | `min`, `max`, `tooltip.showMode` |
| `dxRangeSlider` | Aralık kaydırıcı | Sayı | `min`, `max`, `showRange` | | `dxRangeSlider` | Aralık kaydırıcı | Sayı | `min`, `max`, `showRange` |
| `dxRating` | Yıldızlı puan (platform). Tıklanan yıldızın sırası (1..`count`) tam sayı olarak kaydedilir; seçili yıldıza yeniden tıklamak değeri boşaltır (`allowClear: false` kapatır). Liste/kart hücresinde yıldız olarak çizilir; yıldız şablonu olmayan görünümde (Scheduler) sayı kutusudur | Int32 (11) | `count` (varsayılan 5), `allowClear` |
| `dxHtmlEditor` | Zengin metin | String (16) | `toolbar.items`, `toolbar.multiline`, `valueType` (`html`/`markdown`), `mediaResizing.enabled`, `imageUpload.tabs`, `imageUpload.uploadUrl`, `imageUpload.fileUploadMode`, `tableResizing.enabled`, `tableContextMenu.enabled`. Boş `editorOptions` da tam araç çubuğu, dosyadan/URL'den resim, resim boyutlandırma ve tablo araçlarıyla gelir (`ui/src/views/shared/htmlEditorOptions.ts`); yazılan değer varsayılanın üstüne tek seviye birleştirilir, yalnızca farkı yaz. | | `dxHtmlEditor` | Zengin metin | String (16) | `toolbar.items`, `toolbar.multiline`, `valueType` (`html`/`markdown`), `mediaResizing.enabled`, `imageUpload.tabs`, `imageUpload.uploadUrl`, `imageUpload.fileUploadMode`, `tableResizing.enabled`, `tableContextMenu.enabled`. Boş `editorOptions` da tam araç çubuğu, dosyadan/URL'den resim, resim boyutlandırma ve tablo araçlarıyla gelir (`ui/src/views/shared/htmlEditorOptions.ts`); yazılan değer varsayılanın üstüne tek seviye birleştirilir, yalnızca farkı yaz. |
| `dxImageUpload` | Görsel yükleme (platform); görsel tutan her kolonun editörü. `accept` belge türlerini de içerirse (`application/pdf,.doc,.docx` — CV, imzalı form) görsel olmayan `data:` değeri listede ve formda belge ikonuyla çizilir, tıklanınca blob olarak yeni sekmede açılır (`imageHoverPreview.thumbnailSource`). `uploadUrl` yoksa dosya **base64** metin olarak kolona yazılır → kolon `nvarchar(MAX)`; sınırlı boylu kolonla deploy reddedilir. `uploadUrl` varsa kolona dönen adres yazılır | String (16) | `uploadUrl`\*, `accept`\*, `capture`\*, `multiple`\*, `maxFileSize`\*, `width`\*, `height`\* | | `dxImageUpload` | Görsel yükleme (platform); görsel tutan her kolonun editörü. `accept` belge türlerini de içerirse (`application/pdf,.doc,.docx` — CV, imzalı form) görsel olmayan `data:` değeri listede ve formda belge ikonuyla çizilir, tıklanınca blob olarak yeni sekmede açılır (`imageHoverPreview.thumbnailSource`). `uploadUrl` yoksa dosya **base64** metin olarak kolona yazılır → kolon `nvarchar(MAX)`; sınırlı boylu kolonla deploy reddedilir. `uploadUrl` varsa kolona dönen adres yazılır | String (16) | `uploadUrl`\*, `accept`\*, `capture`\*, `multiple`\*, `maxFileSize`\*, `width`\*, `height`\* |
| `dxImageViewer` | Görsel gösterimi (platform), yükleme yapmaz. Tablo kolonuna bağlı alanda kullanılmaz — görsel kolonu `dxImageUpload`'dır, deploy `dxImageViewer`'ı reddeder | String (16) | `width`, `height` | | `dxImageViewer` | Görsel gösterimi (platform), yükleme yapmaz. Tablo kolonuna bağlı alanda kullanılmaz — görsel kolonu `dxImageUpload`'dır, deploy `dxImageViewer`'ı reddeder | String (16) | `width`, `height` |
@ -1536,7 +1538,7 @@ editör yetenekleri ortak koddan gelir; bir tarafa yeni yetenek ortak yardımcı
| Buton `onClick` (fonksiyon metni, `@SCAN_BARCODE`) ve bağlamı | `resolveEditorButtons` + `withButtonContext` | | Buton `onClick` (fonksiyon metni, `@SCAN_BARCODE`) ve bağlamı | `resolveEditorButtons` + `withButtonContext` |
| Cascade temizleme/kilit | `list/shared/cascade.ts` | | Cascade temizleme/kilit | `list/shared/cascade.ts` |
| Grid/Tree `onEditorPreparing` gövdesi | `prepareEditingEditor` (`list/shared/editingFormScripts.ts`) | | Grid/Tree `onEditorPreparing` gövdesi | `prepareEditingEditor` (`list/shared/editingFormScripts.ts`) |
| TagBox, GridBox, ImageUpload, ImageViewer | Tek uygulama `form/editors/*`; liste hücresi `list/editors/cellEditors.tsx` adaptörüyle bağlanır | | TagBox, GridBox, ImageUpload, ImageViewer, Rating | Tek uygulama `form/editors/*`; liste hücresi `list/editors/cellEditors.tsx` adaptörüyle bağlanır |
DataGrid/TreeList platform editörlerini `editCellTemplate` ile çizer ve `onEditorPreparing` onlar için DataGrid/TreeList platform editörlerini `editCellTemplate` ile çizer ve `onEditorPreparing` onlar için
çalışmaz; salt-okunur/devre dışı kararı, cascade ve `EditorScript` bu editörlere çalışmaz; salt-okunur/devre dışı kararı, cascade ve `EditorScript` bu editörlere
@ -1913,7 +1915,8 @@ cevaba yazılmaz.
], ],
"WorkflowDto": { "ApprovalUserFieldName": "", "ApprovalDateFieldName": "", "WorkflowDto": { "ApprovalUserFieldName": "", "ApprovalDateFieldName": "",
"ApprovalStatusFieldName": "", "ApprovalDescriptionFieldName": "", "ApprovalStatusFieldName": "", "ApprovalDescriptionFieldName": "",
"ApprovalIsFilterUserName": false, "ApprovalIsResetWorkflow": false, "Criteria": [] } "ApprovalIsFilterUserName": false, "ApprovalIsResetWorkflow": false,
"ApprovalIsManualStart": false, "Criteria": [] }
} }
} }
``` ```
@ -2288,6 +2291,7 @@ wizard'ınkinden **küçük** olmalıdır (§0.2).
| `ApprovalDescriptionFieldName` | Onay/ret açıklaması | | `ApprovalDescriptionFieldName` | Onay/ret açıklaması |
| `ApprovalIsFilterUserName` | Liste yalnızca kullanıcının onayına düşenleri göstersin | | `ApprovalIsFilterUserName` | Liste yalnızca kullanıcının onayına düşenleri göstersin |
| `ApprovalIsResetWorkflow` | Kayıt güncellenince akış başa dönsün | | `ApprovalIsResetWorkflow` | Kayıt güncellenince akış başa dönsün |
| `ApprovalIsManualStart` | Yeni kayıt akışı kendiliğinden başlatmasın; kayıt taslak kalır, akış toolbar'daki başlat düğmesiyle başlar. Onaya gönderilmiş başlığa satır eklemeyi engelleyen alt formu olan ekranlarda (talep + satırlar) açılır; kapalıyken Grid/Tree yeni kaydın ardından akışı başlatır |
`Criteria` düğüm grafiğidir. `Kind` değerleri: **`Start`**, **`Compare`**, **`Approval`**, `Criteria` düğüm grafiğidir. `Kind` değerleri: **`Start`**, **`Compare`**, **`Approval`**,
**`Inform`**, **`End`**. **`Inform`**, **`End`**.
@ -2627,7 +2631,7 @@ Builder'ı (§5) açar — çıktıları `EditorOptions`/`EditorScript` string'l
**Adım 7 — İş Akışı.** Zorunlu ikili: `ApprovalUserFieldName` + `ApprovalStatusFieldName`; **Adım 7 — İş Akışı.** Zorunlu ikili: `ApprovalUserFieldName` + `ApprovalStatusFieldName`;
opsiyonel `ApprovalDateFieldName`, `ApprovalDescriptionFieldName`, `ApprovalIsFilterUserName` opsiyonel `ApprovalDateFieldName`, `ApprovalDescriptionFieldName`, `ApprovalIsFilterUserName`
(liste yalnız sıradaki onaycıya düşsün), `ApprovalIsResetWorkflow` (kayıt değişince akış başa (liste yalnız sıradaki onaycıya düşsün), `ApprovalIsResetWorkflow` (kayıt değişince akış başa
dönsün). Onay düğümleri (`Criteria`) görsel akış tasarımcısında kurulur ve `ListFormWorkflow` dönsün), `ApprovalIsManualStart` (yeni kayıt taslak kalsın, akış başlat düğmesiyle başlasın). Onay düğümleri (`Criteria`) görsel akış tasarımcısında kurulur ve `ListFormWorkflow`
kayıtlarına gider (§4.7). kayıtlarına gider (§4.7).
**Adım 12 — Deploy.** Sol tarafta tüm adımların özeti, sağda üretim log'u. Log, deploy'un seed **Adım 12 — Deploy.** Sol tarafta tüm adımların özeti, sağda üretim log'u. Log, deploy'un seed
@ -2986,7 +2990,7 @@ Dizi elemanları yaprak sayılmaz; `toolbar.items` tek bir `json` alanıdır.
| TagBox | `showSelectionControls`\*, `maxDisplayedTags`\*, `showMultiTagOnly`\*, `applyValueMode`\*, `multiline`, `hideSelectedItems` | `dxTagBox` | | TagBox | `showSelectionControls`\*, `maxDisplayedTags`\*, `showMultiTagOnly`\*, `applyValueMode`\*, `multiline`, `hideSelectedItems` | `dxTagBox` |
| GridBox | `columns`\*, `selectionMode`\*, `filterRowVisible`\* | `dxGridBox` | | GridBox | `columns`\*, `selectionMode`\*, `filterRowVisible`\* | `dxGridBox` |
| Görsel | `uploadUrl`\*, `fileFieldName`, `accept`\*, `capture`\*, `multiple`\*, `maxFileSize`\* | `dxImageUpload`, `dxImageViewer` | | Görsel | `uploadUrl`\*, `fileFieldName`, `accept`\*, `capture`\*, `multiple`\*, `maxFileSize`\* | `dxImageUpload`, `dxImageViewer` |
| Seçim | `text` (`dxCheckBox`), `switchedOnText`/`switchedOffText` (`dxSwitch`), `layout` (`dxRadioGroup`), `editAlphaChannel`/`keyStep` (`dxColorBox`) | ilgili editör | | Seçim | `text` (`dxCheckBox`), `switchedOnText`/`switchedOffText` (`dxSwitch`), `layout` (`dxRadioGroup`), `editAlphaChannel`/`keyStep` (`dxColorBox`), `count`/`allowClear` (`dxRating`) | ilgili editör |
| Kaydırıcı | `min`, `max`, `step`, `tooltip.enabled`, `tooltip.showMode`, `showRange` | `dxSlider`, `dxRangeSlider` | | Kaydırıcı | `min`, `max`, `step`, `tooltip.enabled`, `tooltip.showMode`, `showRange` | `dxSlider`, `dxRangeSlider` |
| HTML | `valueType`, `toolbar.multiline`, `toolbar.items`, `mediaResizing.enabled`, `imageUpload.tabs`, `imageUpload.fileUploadMode`, `imageUpload.uploadUrl`, `tableResizing.enabled`, `tableContextMenu.enabled` | `dxHtmlEditor` | | HTML | `valueType`, `toolbar.multiline`, `toolbar.items`, `mediaResizing.enabled`, `imageUpload.tabs`, `imageUpload.fileUploadMode`, `imageUpload.uploadUrl`, `tableResizing.enabled`, `tableContextMenu.enabled` | `dxHtmlEditor` |
| Liste sütunu | `format` (metin hâli), `encodeHtml`, `buttons` | hepsi — bu üçü **grid sütununu** da etkiler | | Liste sütunu | `format` (metin hâli), `encodeHtml`, `buttons` | hepsi — bu üçü **grid sütununu** da etkiler |

View file

@ -444,7 +444,7 @@ ve atanan kullanıcı alanlarından kurulur.
- **Editör tipleri:** `dxTextBox`, `dxTextArea`, `dxNumberBox`, `dxDateBox`, `dxDateRangeBox`, - **Editör tipleri:** `dxTextBox`, `dxTextArea`, `dxNumberBox`, `dxDateBox`, `dxDateRangeBox`,
`dxCalendar`, `dxCheckBox`, `dxSwitch`, `dxRadioGroup`, `dxSelectBox`, `dxTagBox`, `dxLookup`, `dxCalendar`, `dxCheckBox`, `dxSwitch`, `dxRadioGroup`, `dxSelectBox`, `dxTagBox`, `dxLookup`,
`dxDropDownBox`, `dxGridBox`, `dxAutocomplete`, `dxColorBox`, `dxHtmlEditor`, `dxSlider`, `dxDropDownBox`, `dxGridBox`, `dxAutocomplete`, `dxColorBox`, `dxHtmlEditor`, `dxSlider`,
`dxRangeSlider` — ayrıca görsel yükleme/görüntüleme editörleri. `dxRangeSlider` — ayrıca görsel yükleme/görüntüleme editörleri ve yıldızlı puan (`dxRating`).
- **Lookup kaynakları:** `StaticData`, `Query`, `WebService`; ebeveyn–çocuk (cascade) desteklidir. - **Lookup kaynakları:** `StaticData`, `Query`, `WebService`; ebeveyn–çocuk (cascade) desteklidir.
Cascade liste popup'ında, satır içi düzenlemede ve form sayfasında (`/admin/form/...`, `FormDevExpress`) Cascade liste popup'ında, satır içi düzenlemede ve form sayfasında (`/admin/form/...`, `FormDevExpress`)
aynı kuralla çalışır: alan değişince ebeveyni o alan olan çocuklar ve alanın `CascadeEmptyFields` aynı kuralla çalışır: alan değişince ebeveyni o alan olan çocuklar ve alanın `CascadeEmptyFields`

View file

@ -11,5 +11,11 @@ public class WorkflowDto
public bool ApprovalIsFilterUserName { get; set; } public bool ApprovalIsFilterUserName { get; set; }
public bool ApprovalIsResetWorkflow { get; set; } public bool ApprovalIsResetWorkflow { get; set; }
/// <summary>
/// Açıksa yeni kayıt akışı kendiliğinden başlatmaz: kayıt taslak kalır, alt form satırları
/// onaydan önce girilebilir ve akış toolbar'daki başlat düğmesiyle başlar.
/// </summary>
public bool ApprovalIsManualStart { get; set; }
public List<ListFormWorkflowCriteriaDto> Criteria { get; set; } = []; public List<ListFormWorkflowCriteriaDto> Criteria { get; set; } = [];
} }

View file

@ -1023,6 +1023,7 @@ public static class PlatformConsts
public const string dxTextBox = "dxTextBox"; public const string dxTextBox = "dxTextBox";
public const string dxImageViewer = "dxImageViewer"; public const string dxImageViewer = "dxImageViewer";
public const string dxImageUpload = "dxImageUpload"; public const string dxImageUpload = "dxImageUpload";
public const string dxRating = "dxRating";
} }
public static class CustomEndpointConsts public static class CustomEndpointConsts

View file

@ -11,6 +11,7 @@ public class Workflow : ValueObject
public string ApprovalDescriptionFieldName { get; set; } public string ApprovalDescriptionFieldName { get; set; }
public bool ApprovalIsFilterUserName { get; set; } public bool ApprovalIsFilterUserName { get; set; }
public bool ApprovalIsResetWorkflow { get; set; } public bool ApprovalIsResetWorkflow { get; set; }
public bool ApprovalIsManualStart { get; set; }
protected override IEnumerable<object> GetAtomicValues() protected override IEnumerable<object> GetAtomicValues()
{ {
@ -20,6 +21,7 @@ public class Workflow : ValueObject
yield return ApprovalDescriptionFieldName; yield return ApprovalDescriptionFieldName;
yield return ApprovalIsFilterUserName; yield return ApprovalIsFilterUserName;
yield return ApprovalIsResetWorkflow; yield return ApprovalIsResetWorkflow;
yield return ApprovalIsManualStart;
} }
} }

View file

@ -272,7 +272,7 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
`phoneIntl`, `phoneCountry`) görüntü kuralı iki yerde yazılır: liste/kart istemcide, dinamik rapor sunucuda. `phoneIntl`, `phoneCountry`) görüntü kuralı iki yerde yazılır: liste/kart istemcide, dinamik rapor sunucuda.
Biri değişirse diğeri de değişir; yoksa liste ile rapor aynı numarayı farklı yazar. Biri değişirse diğeri de değişir; yoksa liste ile rapor aynı numarayı farklı yazar.
- **Liste popup'ı (Grid/Tree) ↔ form sayfası (`FormDevExpress`)** — editör yetenekleri ortak yardımcılardadır - **Liste popup'ı (Grid/Tree) ↔ form sayfası (`FormDevExpress`)** — editör yetenekleri ortak yardımcılardadır
(`list/shared/editingForm.ts`, `editingFormScripts.ts`, `cascade.ts`); TagBox/GridBox/ImageUpload/ImageViewer'ın (`list/shared/editingForm.ts`, `editingFormScripts.ts`, `cascade.ts`); TagBox/GridBox/ImageUpload/ImageViewer/Rating'in
tek uygulaması `form/editors/*`, liste hücresi yalnızca `list/editors/cellEditors.tsx` adaptörüdür. Bir tarafa tek uygulaması `form/editors/*`, liste hücresi yalnızca `list/editors/cellEditors.tsx` adaptörüdür. Bir tarafa
yetenek eklenirken ortak yardımcıya eklenir; tek tarafa yazılan yetenek eşliği bozar. Tablo: yetenek eklenirken ortak yardımcıya eklenir; tek tarafa yazılan yetenek eşliği bozar. Tablo:
`lowcode.instructions.md` §4.3.4. `lowcode.instructions.md` §4.3.4.

View file

@ -45770,6 +45770,20 @@
"Key": "App.ListFormEditWorkflow.ApprovalIsResetWorkflow", "Key": "App.ListFormEditWorkflow.ApprovalIsResetWorkflow",
"Value": "İş Akışı Sıfırlama?" "Value": "İş Akışı Sıfırlama?"
}, },
{
"Id": "15424420-968a-5f95-a216-0457cdecd257",
"CultureName": "en",
"ResourceName": "Platform",
"Key": "App.ListFormEditWorkflow.ApprovalIsManualStart",
"Value": "Start Workflow Manually?"
},
{
"Id": "ee2f23f5-49c8-5f81-a72c-10b4595443f4",
"CultureName": "tr",
"ResourceName": "Platform",
"Key": "App.ListFormEditWorkflow.ApprovalIsManualStart",
"Value": "Akış Elle Başlatılsın mı?"
},
{ {
"Id": "b49643d5-247b-586b-a640-6c2ebf3fdcf4", "Id": "b49643d5-247b-586b-a640-6c2ebf3fdcf4",
"CultureName": "en", "CultureName": "en",

View file

@ -78,7 +78,7 @@ Departmanların iç ve dış ihtiyaç duydukları malzemeleri satınalmaya iletm
- Wizard List ekranları + menüsüz satır alt formu + Grid Workflow. Beş liste aynı tabloyu (`Prc_T_MaterialRequest`) filtreyle gösterir. - Wizard List ekranları + menüsüz satır alt formu + Grid Workflow. Beş liste aynı tabloyu (`Prc_T_MaterialRequest`) filtreyle gösterir.
- Belge no numaratörden: `MaterialRequest` (MT-YYYY-00001, yıllık sıfırlanır). Şirket alanı açılmadı (kiracı kapsamı). - Belge no numaratörden: `MaterialRequest` (MT-YYYY-00001, yıllık sıfırlanır). Şirket alanı açılmadı (kiracı kapsamı).
- Departman onaycısı: `Adm_T_Approver`'da Onay Yeri = Satınalma (`Purchase`) ve talebin departmanı. Satınalma onaycısı: Onaycılar'a eklenen yeni Onay Yeri **Satınalma Masası** (`PurchaseDesk`) satırları, departmandan bağımsız. Herhangi bir onaycının onayı yeterli. - Departman onaycısı: `Adm_T_Approver`'da Onay Yeri = Satınalma (`Purchase`) ve talebin departmanı. Satınalma onaycısı: Onaycılar'a eklenen yeni Onay Yeri **Satınalma Masası** (`PurchaseDesk`) satırları, departmandan bağımsız. Herhangi bir onaycının onayı yeterli.
- Taslak aşaması: kayıt Taslak açılır; akış platformun Onaya Gönder işlemiyle başlar. - Taslak aşaması: kayıt Taslak açılır; akış platformun Onaya Gönder işlemiyle başlar (`WorkflowDto.ApprovalIsManualStart: true` — yoksa yeni kayıt akışı kendiliğinden başlatır ve satır girilemez).
- Görseldeki "Kendi Depomda" ve "Tüm Depolar" stok kolonları üretilmedi: uygulamada stok miktarı/hareket tablosu yok; stok modülü gelince ayrı talep. - Görseldeki "Kendi Depomda" ve "Tüm Depolar" stok kolonları üretilmedi: uygulamada stok miktarı/hareket tablosu yok; stok modülü gelince ayrı talep.
- Karşılama Şekli: Satınalma / Depodan Transfer. - Karşılama Şekli: Satınalma / Depodan Transfer.
- Onay akışının "onaylayan" alanı platformda kullanıcı adı (metin) yazdığı için akışa bağlanmadı (`ApprovalUserFieldName` boş); departman/satınalma onaylayanı kayıt sonrası prosedürde kaydı son güncelleyen kullanıcıdan yazılır. `ApproverId` kolonu kullanılmaz. - Onay akışının "onaylayan" alanı platformda kullanıcı adı (metin) yazdığı için akışa bağlanmadı (`ApprovalUserFieldName` boş); departman/satınalma onaylayanı kayıt sonrası prosedürde kaydı son güncelleyen kullanıcıdan yazılır. `ApproverId` kolonu kullanılmaz.
@ -295,7 +295,7 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak | | Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| `RequestNo` | Talep No | dxTextBox | | ✓ | | | `RequestNo` | Talep No | dxTextBox | | ✓ | |
| `Priority` | Öncelik (0-3) | dxNumberBox | ✓ | ✓ | | | `Priority` | Öncelik (0-3) | dxRating | ✓ | ✓ | |
| `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers · script | | `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers · script |
| `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department | | `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department |
| `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse · script | | `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse · script |
@ -340,7 +340,7 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak | | Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| `RequestNo` | Talep No | dxTextBox | | ✓ | | | `RequestNo` | Talep No | dxTextBox | | ✓ | |
| `Priority` | Öncelik (0-3) | dxNumberBox | ✓ | ✓ | | | `Priority` | Öncelik (0-3) | dxRating | ✓ | ✓ | |
| `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers | | `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers |
| `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department | | `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department |
| `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse | | `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse |
@ -378,7 +378,7 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak | | Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| `RequestNo` | Talep No | dxTextBox | | ✓ | | | `RequestNo` | Talep No | dxTextBox | | ✓ | |
| `Priority` | Öncelik (0-3) | dxNumberBox | ✓ | ✓ | | | `Priority` | Öncelik (0-3) | dxRating | ✓ | ✓ | |
| `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers | | `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers |
| `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department | | `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department |
| `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse | | `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse |
@ -418,7 +418,7 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak | | Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| `RequestNo` | Talep No | dxTextBox | | ✓ | | | `RequestNo` | Talep No | dxTextBox | | ✓ | |
| `Priority` | Öncelik (0-3) | dxNumberBox | ✓ | ✓ | | | `Priority` | Öncelik (0-3) | dxRating | ✓ | ✓ | |
| `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers | | `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers |
| `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department | | `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department |
| `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse | | `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse |
@ -456,7 +456,7 @@ Yönetim › Tanımlamalar › Onaycılar (onay yerleri Satınalma ve Satınalma
| Alan | Başlık | Editör | Zorunlu | Formda | Kaynak | | Alan | Başlık | Editör | Zorunlu | Formda | Kaynak |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| `RequestNo` | Talep No | dxTextBox | | ✓ | | | `RequestNo` | Talep No | dxTextBox | | ✓ | |
| `Priority` | Öncelik (0-3) | dxNumberBox | ✓ | ✓ | | | `Priority` | Öncelik (0-3) | dxRating | ✓ | ✓ | |
| `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers · script | | `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers · script |
| `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department | | `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department |
| `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse · script | | `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse · script |

View file

@ -656,6 +656,7 @@
"ApprovalDescriptionFieldName": "ApprovalNote", "ApprovalDescriptionFieldName": "ApprovalNote",
"ApprovalIsFilterUserName": false, "ApprovalIsFilterUserName": false,
"ApprovalIsResetWorkflow": true, "ApprovalIsResetWorkflow": true,
"ApprovalIsManualStart": false,
"Criteria": [ "Criteria": [
{ {
"ListFormCode": "App.Prc.MaterialRequest", "ListFormCode": "App.Prc.MaterialRequest",
@ -1385,7 +1386,7 @@
"Format": null, "Format": null,
"EditOrderNo": 2, "EditOrderNo": 2,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
"EditorType2": "dxNumberBox", "EditorType2": "dxRating",
"ColSpan": 1, "ColSpan": 1,
"EditorOptions": "{\"min\":0,\"max\":3,\"showSpinButtons\":true}", "EditorOptions": "{\"min\":0,\"max\":3,\"showSpinButtons\":true}",
"GridBoxOptions": null, "GridBoxOptions": null,

View file

@ -509,8 +509,8 @@
"Order": 1, "Order": 1,
"ItemType": "group", "ItemType": "group",
"Caption": "", "Caption": "",
"ColCount": 2, "ColCount": 4,
"ColSpan": 2 "ColSpan": 4
} }
], ],
"PermissionDto": { "PermissionDto": {
@ -575,6 +575,7 @@
"ApprovalDescriptionFieldName": "ApprovalNote", "ApprovalDescriptionFieldName": "ApprovalNote",
"ApprovalIsFilterUserName": true, "ApprovalIsFilterUserName": true,
"ApprovalIsResetWorkflow": true, "ApprovalIsResetWorkflow": true,
"ApprovalIsManualStart": false,
"Criteria": [ "Criteria": [
{ {
"ListFormCode": "App.Prc.MaterialRequestDeptApproval", "ListFormCode": "App.Prc.MaterialRequestDeptApproval",
@ -1036,7 +1037,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": null, "EditOrderNo": null,
"EditGroupOrderNo": null, "EditGroupOrderNo": null,
@ -1162,7 +1163,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 1, "EditOrderNo": 1,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -1300,11 +1301,11 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 2, "EditOrderNo": 2,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
"EditorType2": "dxNumberBox", "EditorType2": "dxRating",
"ColSpan": 1, "ColSpan": 1,
"EditorOptions": "{\"readOnly\":true}", "EditorOptions": "{\"readOnly\":true}",
"GridBoxOptions": null, "GridBoxOptions": null,
@ -1438,7 +1439,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 3, "EditOrderNo": 3,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -1576,7 +1577,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 4, "EditOrderNo": 4,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -1714,7 +1715,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 5, "EditOrderNo": 5,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -1852,7 +1853,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 6, "EditOrderNo": 6,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -1990,7 +1991,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 7, "EditOrderNo": 7,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -2128,7 +2129,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 8, "EditOrderNo": 8,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -2254,7 +2255,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 9, "EditOrderNo": 9,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -2380,12 +2381,12 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 10, "EditOrderNo": 10,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
"EditorType2": "dxSelectBox", "EditorType2": "dxSelectBox",
"ColSpan": 1, "ColSpan": 3,
"EditorOptions": "{\"showClearButton\":true}", "EditorOptions": "{\"showClearButton\":true}",
"GridBoxOptions": null, "GridBoxOptions": null,
"TagBoxOptions": null, "TagBoxOptions": null,
@ -2506,12 +2507,12 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 11, "EditOrderNo": 11,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
"EditorType2": "dxTextArea", "EditorType2": "dxTextArea",
"ColSpan": 2, "ColSpan": 4,
"EditorOptions": "{\"readOnly\":true}", "EditorOptions": "{\"readOnly\":true}",
"GridBoxOptions": null, "GridBoxOptions": null,
"TagBoxOptions": null, "TagBoxOptions": null,
@ -2632,7 +2633,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": null, "EditOrderNo": null,
"EditGroupOrderNo": null, "EditGroupOrderNo": null,
@ -2758,7 +2759,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": null, "EditOrderNo": null,
"EditGroupOrderNo": null, "EditGroupOrderNo": null,
@ -2884,7 +2885,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": null, "EditOrderNo": null,
"EditGroupOrderNo": null, "EditGroupOrderNo": null,
@ -3010,7 +3011,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": null, "EditOrderNo": null,
"EditGroupOrderNo": null, "EditGroupOrderNo": null,

View file

@ -586,6 +586,7 @@
"ApprovalDescriptionFieldName": null, "ApprovalDescriptionFieldName": null,
"ApprovalIsFilterUserName": false, "ApprovalIsFilterUserName": false,
"ApprovalIsResetWorkflow": false, "ApprovalIsResetWorkflow": false,
"ApprovalIsManualStart": false,
"Criteria": [] "Criteria": []
}, },
"UserId": null, "UserId": null,
@ -1224,7 +1225,7 @@
"Format": null, "Format": null,
"EditOrderNo": 2, "EditOrderNo": 2,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
"EditorType2": "dxNumberBox", "EditorType2": "dxRating",
"ColSpan": 1, "ColSpan": 1,
"EditorOptions": "{\"readOnly\":true}", "EditorOptions": "{\"readOnly\":true}",
"GridBoxOptions": null, "GridBoxOptions": null,

View file

@ -604,6 +604,7 @@
"ApprovalDescriptionFieldName": "ApprovalNote", "ApprovalDescriptionFieldName": "ApprovalNote",
"ApprovalIsFilterUserName": true, "ApprovalIsFilterUserName": true,
"ApprovalIsResetWorkflow": true, "ApprovalIsResetWorkflow": true,
"ApprovalIsManualStart": false,
"Criteria": [ "Criteria": [
{ {
"ListFormCode": "App.Prc.MaterialRequestPurchaseApproval", "ListFormCode": "App.Prc.MaterialRequestPurchaseApproval",
@ -1065,7 +1066,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": null, "EditOrderNo": null,
"EditGroupOrderNo": null, "EditGroupOrderNo": null,
@ -1191,7 +1192,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 1, "EditOrderNo": 1,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -1329,11 +1330,11 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 2, "EditOrderNo": 2,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
"EditorType2": "dxNumberBox", "EditorType2": "dxRating",
"ColSpan": 1, "ColSpan": 1,
"EditorOptions": "{\"readOnly\":true}", "EditorOptions": "{\"readOnly\":true}",
"GridBoxOptions": null, "GridBoxOptions": null,
@ -1467,7 +1468,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 3, "EditOrderNo": 3,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -1605,7 +1606,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 4, "EditOrderNo": 4,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -1743,7 +1744,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 5, "EditOrderNo": 5,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -1881,7 +1882,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 6, "EditOrderNo": 6,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -2019,7 +2020,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 7, "EditOrderNo": 7,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -2157,7 +2158,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 8, "EditOrderNo": 8,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -2283,7 +2284,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 9, "EditOrderNo": 9,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -2409,7 +2410,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 10, "EditOrderNo": 10,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -2535,7 +2536,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 11, "EditOrderNo": 11,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
@ -2661,7 +2662,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": null, "EditOrderNo": null,
"EditGroupOrderNo": null, "EditGroupOrderNo": null,
@ -2787,7 +2788,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": null, "EditOrderNo": null,
"EditGroupOrderNo": null, "EditGroupOrderNo": null,
@ -2913,7 +2914,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": null, "EditOrderNo": null,
"EditGroupOrderNo": null, "EditGroupOrderNo": null,
@ -3039,7 +3040,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": null, "EditOrderNo": null,
"EditGroupOrderNo": null, "EditGroupOrderNo": null,
@ -3165,7 +3166,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 1, "EditOrderNo": 1,
"EditGroupOrderNo": 2, "EditGroupOrderNo": 2,
@ -3291,7 +3292,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 2, "EditOrderNo": 2,
"EditGroupOrderNo": 2, "EditGroupOrderNo": 2,
@ -3417,7 +3418,7 @@
"GroupInterval": "", "GroupInterval": "",
"WordWrapEnabled": false "WordWrapEnabled": false
}, },
"Alignment": null, "Alignment": "left",
"Format": null, "Format": null,
"EditOrderNo": 3, "EditOrderNo": 3,
"EditGroupOrderNo": 2, "EditGroupOrderNo": 2,

View file

@ -670,12 +670,13 @@
"DefaultLayout": "grid" "DefaultLayout": "grid"
}, },
"WorkflowDto": { "WorkflowDto": {
"ApprovalUserFieldName": null, "ApprovalUserFieldName": "ApproverId",
"ApprovalDateFieldName": "ApprovalDate", "ApprovalDateFieldName": "ApprovalDate",
"ApprovalStatusFieldName": "ApprovalStatus", "ApprovalStatusFieldName": "ApprovalStatus",
"ApprovalDescriptionFieldName": "ApprovalNote", "ApprovalDescriptionFieldName": "ApprovalNote",
"ApprovalIsFilterUserName": false, "ApprovalIsFilterUserName": false,
"ApprovalIsResetWorkflow": true, "ApprovalIsResetWorkflow": true,
"ApprovalIsManualStart": false,
"Criteria": [ "Criteria": [
{ {
"ListFormCode": "App.Prc.MyMaterialRequest", "ListFormCode": "App.Prc.MyMaterialRequest",
@ -1405,7 +1406,7 @@
"Format": null, "Format": null,
"EditOrderNo": 2, "EditOrderNo": 2,
"EditGroupOrderNo": 1, "EditGroupOrderNo": 1,
"EditorType2": "dxNumberBox", "EditorType2": "dxRating",
"ColSpan": 1, "ColSpan": 1,
"EditorOptions": "{\"min\":0,\"max\":3,\"showSpinButtons\":true}", "EditorOptions": "{\"min\":0,\"max\":3,\"showSpinButtons\":true}",
"GridBoxOptions": null, "GridBoxOptions": null,

View file

@ -1,15 +1,9 @@
import React, { import React, { forwardRef, useState, useCallback, type KeyboardEvent, type ReactNode } from 'react'
forwardRef,
useState,
useCallback,
useRef,
type KeyboardEvent,
type ReactNode,
} from 'react'
import classNames from 'classnames' import classNames from 'classnames'
import { useConfig } from '../ConfigProvider' import { useConfig } from '../ConfigProvider'
import type { CommonProps } from '../@types/common' import type { CommonProps } from '../@types/common'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { RATE_STAR_POINTS } from './starShape'
export interface RateProps extends CommonProps { export interface RateProps extends CommonProps {
/** Toplam yıldız sayısı. Varsayılan: 5 */ /** Toplam yıldız sayısı. Varsayılan: 5 */
@ -59,7 +53,7 @@ const StarIcon = ({ filled, half, size }: { filled: boolean; half: boolean; size
</defs> </defs>
)} )}
<polygon <polygon
points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26" points={RATE_STAR_POINTS}
fill={half ? 'url(#rate-half)' : filled ? 'currentColor' : 'none'} fill={half ? 'url(#rate-half)' : filled ? 'currentColor' : 'none'}
stroke="currentColor" stroke="currentColor"
strokeWidth="1.5" strokeWidth="1.5"
@ -95,7 +89,6 @@ const Rate = forwardRef<HTMLDivElement, RateProps>((props, ref) => {
const isControlled = valueProp !== undefined const isControlled = valueProp !== undefined
const [internalValue, setInternalValue] = useState(defaultValue) const [internalValue, setInternalValue] = useState(defaultValue)
const [hoverValue, setHoverValue] = useState<number | null>(null) const [hoverValue, setHoverValue] = useState<number | null>(null)
const lastClickedRef = useRef<number | null>(null)
const value = isControlled ? valueProp! : internalValue const value = isControlled ? valueProp! : internalValue
const displayValue = hoverValue !== null ? hoverValue : value const displayValue = hoverValue !== null ? hoverValue : value
@ -139,18 +132,14 @@ const Rate = forwardRef<HTMLDivElement, RateProps>((props, ref) => {
const offsetX = e.clientX - rect.left const offsetX = e.clientX - rect.left
const clickedVal = getStarValue(index, offsetX, rect.width) const clickedVal = getStarValue(index, offsetX, rect.width)
let newVal = clickedVal // Seçili yıldıza yeniden tıklamak değeri sıfırlar; karşılaştırma son tıklamayla değil
if (allowClear && clickedVal === lastClickedRef.current) { // güncel değerle yapılır, yoksa kontrollü kullanımda dışarıdan gelen değer temizlenemez.
newVal = 0 const newVal = allowClear && clickedVal === value ? 0 : clickedVal
lastClickedRef.current = null
} else {
lastClickedRef.current = clickedVal
}
if (!isControlled) setInternalValue(newVal) if (!isControlled) setInternalValue(newVal)
onChange?.(newVal) onChange?.(newVal)
}, },
[disabled, readOnly, allowClear, allowHalf, isControlled, onChange], [disabled, readOnly, allowClear, allowHalf, value, isControlled, onChange],
) )
const handleKeyDown = useCallback( const handleKeyDown = useCallback(

View file

@ -0,0 +1,3 @@
/** Yıldız çokgeni; `Rate` ve liste hücresi (DOM şablonu) aynı şekli çizer. */
export const RATE_STAR_POINTS =
'12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26'

View file

@ -871,6 +871,7 @@ export enum PlatformEditorTypes {
dxGridBox = 'dxGridBox', dxGridBox = 'dxGridBox',
dxImageViewer = 'dxImageViewer', dxImageViewer = 'dxImageViewer',
dxImageUpload = 'dxImageUpload', dxImageUpload = 'dxImageUpload',
dxRating = 'dxRating',
} }
export enum GanttScaleTypeEnum { export enum GanttScaleTypeEnum {
@ -948,6 +949,7 @@ export interface WorkflowDto {
approvalDescriptionFieldName: string approvalDescriptionFieldName: string
approvalIsFilterUserName: boolean approvalIsFilterUserName: boolean
approvalIsResetWorkflow: boolean approvalIsResetWorkflow: boolean
approvalIsManualStart: boolean
criteria: ListFormWorkflowCriteriaDto[] criteria: ListFormWorkflowCriteriaDto[]
} }

View file

@ -319,14 +319,25 @@ export function FormTabWorkflow(
approvalDescriptionFieldName: string(), approvalDescriptionFieldName: string(),
approvalIsFilterUserName: bool(), approvalIsFilterUserName: bool(),
approvalIsResetWorkflow: bool(), approvalIsResetWorkflow: bool(),
approvalIsManualStart: bool(),
}), }),
}) })
const initialValues = useStoreState((s) => s.admin.lists.values) const listValues = useStoreState((s) => s.admin.lists.values)
if (!initialValues) { if (!listValues) {
return null return null
} }
// Seçenek eklenmeden önce kaydedilmiş WorkflowJson'da alan yoktur; değeri tanımsız checkbox'ı
// Formik dizi olarak yazar (["on"]) ve boolean doğrulaması düşer.
const initialValues = {
...listValues,
workflowDto: {
...listValues.workflowDto,
approvalIsManualStart: Boolean(listValues.workflowDto?.approvalIsManualStart),
},
}
return ( return (
<div> <div>
<Formik <Formik
@ -341,7 +352,7 @@ export function FormTabWorkflow(
<Form> <Form>
<FormContainer size="sm"> <FormContainer size="sm">
<Card className="my-2"> <Card className="my-2">
<div className="grid grid-cols-1 md:grid-cols-6 gap-2"> <div className="grid grid-cols-1 md:grid-cols-4 xl:grid-cols-7 gap-2">
<FormItem <FormItem
asterisk asterisk
label={translate('::App.ListFormEditWorkflow.ApprovalUserFieldName')} label={translate('::App.ListFormEditWorkflow.ApprovalUserFieldName')}
@ -468,6 +479,19 @@ export function FormTabWorkflow(
> >
<Field name="workflowDto.approvalIsResetWorkflow" component={Checkbox} /> <Field name="workflowDto.approvalIsResetWorkflow" component={Checkbox} />
</FormItem> </FormItem>
<FormItem
label={translate('::App.ListFormEditWorkflow.ApprovalIsManualStart')}
invalid={
!!(
errors.workflowDto?.approvalIsManualStart &&
touched.workflowDto?.approvalIsManualStart
)
}
errorMessage={errors.workflowDto?.approvalIsManualStart as string}
>
<Field name="workflowDto.approvalIsManualStart" component={Checkbox} />
</FormItem>
</div> </div>
<Button block size="sm" variant="solid" type="submit" loading={isSubmitting}> <Button block size="sm" variant="solid" type="submit" loading={isSubmitting}>

View file

@ -448,7 +448,8 @@ function FormFields({
<Container className="h-full"> <Container className="h-full">
<AdaptableCard className="h-full" bodyClass="h-full"> <AdaptableCard className="h-full" bodyClass="h-full">
<div className="grid 2xl:grid-cols-5 xl:grid-cols-3 lg:grid-cols-5 gap-4 h-full border-t-2"> <div className="grid 2xl:grid-cols-5 xl:grid-cols-3 lg:grid-cols-5 gap-4 h-full border-t-2">
<div className="2xl:col-span-2 xl:col-span-1 lg:col-span-2 border-r-2"> {/* Liste sayfayı uzatmaz: sütun ekran yüksekliğiyle sınırlanır, alanlar kendi içinde kayar. */}
<div className="2xl:col-span-2 xl:col-span-1 lg:col-span-2 border-r-2 flex flex-col lg:h-[calc(100vh-350px)] lg:min-h-[400px]">
<div className="flex justify-end px-2 py-2"> <div className="flex justify-end px-2 py-2">
<Segment <Segment
size="xs" size="xs"
@ -469,6 +470,7 @@ function FormFields({
</Segment.Item> </Segment.Item>
</Segment> </Segment>
</div> </div>
<div className="flex-1 min-h-0 overflow-y-auto">
<DndContext <DndContext
sensors={sensors} sensors={sensors}
collisionDetection={closestCenter} collisionDetection={closestCenter}
@ -479,7 +481,7 @@ function FormFields({
strategy={verticalListSortingStrategy} strategy={verticalListSortingStrategy}
> >
<Table compact> <Table compact>
<THead> <THead className="sticky top-0 z-10">
<Tr> <Tr>
<Th className="w-3/12"> <Th className="w-3/12">
<div> <div>
@ -563,6 +565,7 @@ function FormFields({
</SortableContext> </SortableContext>
</DndContext> </DndContext>
</div> </div>
</div>
<div className="2xl:col-span-3 lg:col-span-3 xl:col-span-2"> <div className="2xl:col-span-3 lg:col-span-3 xl:col-span-2">
<FormFieldEdit <FormFieldEdit
item={row} item={row}

View file

@ -138,6 +138,7 @@ const DROPDOWN_EDITORS = [
const DATE_EDITORS = ['dxDateBox', 'dxCalendar', 'dxDateRangeBox'] const DATE_EDITORS = ['dxDateBox', 'dxCalendar', 'dxDateRangeBox']
const IMAGE_EDITORS = ['dxImageUpload', 'dxImageViewer'] const IMAGE_EDITORS = ['dxImageUpload', 'dxImageViewer']
const SLIDER_EDITORS = ['dxSlider', 'dxRangeSlider'] const SLIDER_EDITORS = ['dxSlider', 'dxRangeSlider']
const RATING_EDITORS = ['dxRating']
/* ------------------------------------------------------------------ * /* ------------------------------------------------------------------ *
* Ayar sözlüğü * Ayar sözlüğü
@ -855,6 +856,14 @@ export const optionSpecs: OptionSpec[] = [
editors: ['dxColorBox'], editors: ['dxColorBox'],
}, },
{ path: 'keyStep', label: 'keyStep', type: 'number', group: 'choice', editors: ['dxColorBox'] }, { path: 'keyStep', label: 'keyStep', type: 'number', group: 'choice', editors: ['dxColorBox'] },
{ path: 'count', label: 'count', type: 'number', group: 'choice', editors: RATING_EDITORS },
{
path: 'allowClear',
label: 'allowClear',
type: 'boolean',
group: 'choice',
editors: RATING_EDITORS,
},
// ── Kaydırıcı ──────────────────────────────────────────────────── // ── Kaydırıcı ────────────────────────────────────────────────────
{ path: 'min', label: 'min', type: 'number', group: 'slider', editors: SLIDER_EDITORS }, { path: 'min', label: 'min', type: 'number', group: 'slider', editors: SLIDER_EDITORS },

View file

@ -162,6 +162,7 @@ export const columnEditorTypeListOptions = [
{ value: PlatformEditorTypes.dxGridBox, label: PlatformEditorTypes.dxGridBox }, { value: PlatformEditorTypes.dxGridBox, label: PlatformEditorTypes.dxGridBox },
{ value: PlatformEditorTypes.dxImageViewer, label: PlatformEditorTypes.dxImageViewer }, { value: PlatformEditorTypes.dxImageViewer, label: PlatformEditorTypes.dxImageViewer },
{ value: PlatformEditorTypes.dxImageUpload, label: PlatformEditorTypes.dxImageUpload }, { value: PlatformEditorTypes.dxImageUpload, label: PlatformEditorTypes.dxImageUpload },
{ value: PlatformEditorTypes.dxRating, label: PlatformEditorTypes.dxRating },
{ value: 'dxHtmlEditor', label: 'dxHtmlEditor' }, { value: 'dxHtmlEditor', label: 'dxHtmlEditor' },
{ value: 'dxLookup', label: 'dxLookup' }, { value: 'dxLookup', label: 'dxLookup' },
{ value: 'dxNumberBox', label: 'dxNumberBox' }, { value: 'dxNumberBox', label: 'dxNumberBox' },

View file

@ -155,6 +155,7 @@ const initialValues: ListFormWizardDto = {
approvalDescriptionFieldName: '', approvalDescriptionFieldName: '',
approvalIsFilterUserName: false, approvalIsFilterUserName: false,
approvalIsResetWorkflow: false, approvalIsResetWorkflow: false,
approvalIsManualStart: false,
criteria: [], criteria: [],
}, },
menuEn: '', menuEn: '',
@ -445,6 +446,7 @@ const Wizard = () => {
approvalDescriptionFieldName: '', approvalDescriptionFieldName: '',
approvalIsFilterUserName: false, approvalIsFilterUserName: false,
approvalIsResetWorkflow: false, approvalIsResetWorkflow: false,
approvalIsManualStart: false,
criteria: [], criteria: [],
}) })
const [criteria, setCriteria] = useState<ListFormWorkflowCriteriaDto[]>([]) const [criteria, setCriteria] = useState<ListFormWorkflowCriteriaDto[]>([])

View file

@ -391,7 +391,7 @@ function WizardStepWorkflow({
<Form> <Form>
<Card className="mb-4"> <Card className="mb-4">
<FormContainer> <FormContainer>
<div className="grid grid-cols-1 md:grid-cols-6 gap-4"> <div className="grid grid-cols-1 md:grid-cols-4 xl:grid-cols-7 gap-4">
{[ {[
['approvalUserFieldName', '::App.ListFormEditWorkflow.ApprovalUserFieldName'], ['approvalUserFieldName', '::App.ListFormEditWorkflow.ApprovalUserFieldName'],
[ [
@ -484,6 +484,26 @@ function WizardStepWorkflow({
)} )}
</Field> </Field>
</FormItem> </FormItem>
<FormItem
label={translate('::App.ListFormEditWorkflow.ApprovalIsManualStart')}
>
<Field name="approvalIsManualStart">
{({ field, form }: FieldProps<boolean>) => (
<Checkbox
name={field.name}
checked={Boolean(values.approvalIsManualStart)}
onChange={(checked) => {
form.setFieldValue(field.name, checked)
onWorkflowChange({
...values,
approvalIsManualStart: checked,
criteria,
})
}}
/>
)}
</Field>
</FormItem>
</div> </div>
</FormContainer> </FormContainer>
</Card> </Card>

View file

@ -14,6 +14,7 @@ import { Dispatch, ReactNode, RefObject, useCallback, useEffect, useRef, useStat
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent' import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
import { ImageUploadEditorComponent } from './editors/ImageUploadEditorComponent' import { ImageUploadEditorComponent } from './editors/ImageUploadEditorComponent'
import { ImageViewerEditorComponent } from './editors/ImageViewerEditorComponent' import { ImageViewerEditorComponent } from './editors/ImageViewerEditorComponent'
import { RatingEditorComponent } from './editors/RatingEditorComponent'
import { TagBoxEditorComponent } from './editors/TagBoxEditorComponent' import { TagBoxEditorComponent } from './editors/TagBoxEditorComponent'
import { RowMode, SimpleItemWithColData } from './types' import { RowMode, SimpleItemWithColData } from './types'
import { PlatformEditorTypes } from '@/proxy/form/models' import { PlatformEditorTypes } from '@/proxy/form/models'
@ -47,6 +48,7 @@ const CUSTOM_EDITOR_TYPES = [
PlatformEditorTypes.dxGridBox, PlatformEditorTypes.dxGridBox,
PlatformEditorTypes.dxImageUpload, PlatformEditorTypes.dxImageUpload,
PlatformEditorTypes.dxImageViewer, PlatformEditorTypes.dxImageViewer,
PlatformEditorTypes.dxRating,
] as const ] as const
const FormDevExpress = (props: { const FormDevExpress = (props: {
@ -402,6 +404,14 @@ const FormDevExpress = (props: {
editorOptions={editorOptions} editorOptions={editorOptions}
/> />
) )
case PlatformEditorTypes.dxRating:
return (
<RatingEditorComponent
value={value}
onValueChanged={onChange}
editorOptions={editorOptions}
/>
)
default: default:
return null return null
} }
@ -437,12 +447,13 @@ const FormDevExpress = (props: {
) )
} }
// Görsel editörleri DevExtreme'in bilmediği alan tiplerini yaymamak için // Görsel ve puan editörleri DevExtreme'in bilmediği alan tiplerini yaymamak için
// yalnızca gerekli alanlarla oluşturulur. // yalnızca gerekli alanlarla oluşturulur.
const isImageEditor = const isStandaloneEditor =
formItem.editorType2 === PlatformEditorTypes.dxImageUpload || formItem.editorType2 === PlatformEditorTypes.dxImageUpload ||
formItem.editorType2 === PlatformEditorTypes.dxImageViewer formItem.editorType2 === PlatformEditorTypes.dxImageViewer ||
const itemProps = isImageEditor formItem.editorType2 === PlatformEditorTypes.dxRating
const itemProps = isStandaloneEditor
? { ? {
dataField: formItem.dataField, dataField: formItem.dataField,
name: formItem.name, name: formItem.name,

View file

@ -0,0 +1,44 @@
import { useEffect, useState, type ReactElement } from 'react'
import { Rate } from '@/components/ui'
import { normalizeRatingValue, resolveRatingCount } from '../../list/shared/ratingValue'
/** Form sayfası ve liste düzenleme hücresinin ortak yıldızlı puan editörü; değer 1..count tam sayıdır. */
const RatingEditorComponent = ({
value,
onValueChanged,
editorOptions,
}: {
value: any
onValueChanged: (value: number | null) => void
editorOptions?: any
}): ReactElement => {
const count = resolveRatingCount(editorOptions)
const valueRating = normalizeRatingValue(value, count)
// Grid/Tree hücre şablonu bir kez çizilir ve setValue sonrası `value` prop'u güncellenmez;
// seçim yerel durumda tutulmazsa yıldızlar tıklamadan sonra eski değere döner.
const [rating, setRating] = useState(valueRating)
useEffect(() => {
setRating(valueRating)
}, [valueRating])
return (
<Rate
allowClear={editorOptions?.allowClear !== false}
className="flex items-center min-h-[36px]"
count={count}
disabled={editorOptions?.disabled === true}
readOnly={editorOptions?.readOnly === true}
size={22}
value={rating}
onChange={(next) => {
setRating(next)
// Temizlenen puan 0 değil boş kaydedilir; 0 geçerli bir puan değildir.
onValueChanged(next || null)
}}
/>
)
}
export { RatingEditorComponent }

View file

@ -14,6 +14,7 @@ export type EditorType2 =
| PlatformEditorTypes.dxGridBox | PlatformEditorTypes.dxGridBox
| PlatformEditorTypes.dxImageViewer | PlatformEditorTypes.dxImageViewer
| PlatformEditorTypes.dxImageUpload | PlatformEditorTypes.dxImageUpload
| PlatformEditorTypes.dxRating
export type SimpleItemWithColData = Overwrite< export type SimpleItemWithColData = Overwrite<
SimpleItem, SimpleItem,
{ {

View file

@ -34,14 +34,18 @@ import DxCardView, {
} from 'devextreme-react/card-view' } from 'devextreme-react/card-view'
import CustomStore from 'devextreme/data/custom_store' import CustomStore from 'devextreme/data/custom_store'
import type { DataType } from 'devextreme/common' import type { DataType } from 'devextreme/common'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useCallback, useEffect, useMemo, useRef, useState, type ReactElement } from 'react'
import { createRoot } from 'react-dom/client' import { createRoot } from 'react-dom/client'
import PageTitle from '@/components/shared/PageTitle' import PageTitle from '@/components/shared/PageTitle'
import { NotePanel } from '../form/notes/NotePanel' import { NotePanel } from '../form/notes/NotePanel'
import SubForms from '../form/SubForms' import SubForms from '../form/SubForms'
import GridFilterDialogs from './GridFilterDialogs' import GridFilterDialogs from './GridFilterDialogs'
import { GridExtraFilterToolbar } from './GridExtraFilterToolbar' import { GridExtraFilterToolbar } from './GridExtraFilterToolbar'
import { ImageUploadEditorComponent, ImageViewerEditorComponent } from './editors/cellEditors' import {
ImageUploadEditorComponent,
ImageViewerEditorComponent,
RatingEditorComponent,
} from './editors/cellEditors'
import { import {
autoNumber, autoNumber,
extractSearchParamsFields, extractSearchParamsFields,
@ -93,9 +97,11 @@ const toCardColumn = (
const dataField = column.dataField const dataField = column.dataField
const originalCustomizeText = const originalCustomizeText =
typeof column.customizeText === 'function' ? column.customizeText : undefined typeof column.customizeText === 'function' ? column.customizeText : undefined
const isImageColumn = // Görsel ve puan kolonlarının hücre şablonu kartta da aynı çizimi yapar.
const hasCellTemplate =
typeof column.cellTemplate === 'function' && typeof column.cellTemplate === 'function' &&
Object.prototype.hasOwnProperty.call(column.extras ?? {}, 'imageUploadOptions') (Object.prototype.hasOwnProperty.call(column.extras ?? {}, 'imageUploadOptions') ||
column.colData?.editorType2 === PlatformEditorTypes.dxRating)
const isHtmlColumn = column.colData?.editorType2 === 'dxHtmlEditor' const isHtmlColumn = column.colData?.editorType2 === 'dxHtmlEditor'
return Object.fromEntries( return Object.fromEntries(
@ -129,8 +135,7 @@ const toCardColumn = (
dataType: column.dataType, dataType: column.dataType,
editorOptions: column.editorOptions, editorOptions: column.editorOptions,
filterValue: column.filterValue, filterValue: column.filterValue,
fieldValueTemplate: fieldValueTemplate: hasCellTemplate
isImageColumn && typeof column.cellTemplate === 'function'
? (templateOptions: any) => { ? (templateOptions: any) => {
const templateData = templateOptions?.model ?? templateOptions const templateData = templateOptions?.model ?? templateOptions
const container = document.createElement('div') const container = document.createElement('div')
@ -545,7 +550,9 @@ const CardView = (props: CardViewProps) => {
const commandButtonsRef = useRef(commandButtons) const commandButtonsRef = useRef(commandButtons)
commandButtonsRef.current = commandButtons commandButtonsRef.current = commandButtons
const renderImageUploadEditor = useCallback((templateData: any) => { /** Değer yazan platform editörünü (görsel yükleme, puan) kart formuna bağlar. */
const renderValueEditor = useCallback(
(templateData: any, Editor: (cell: any) => ReactElement) => {
const configuredDataField = const configuredDataField =
templateData?.dataField ?? templateData?.dataField ??
templateData?.item?.dataField ?? templateData?.item?.dataField ??
@ -560,7 +567,7 @@ const CardView = (props: CardViewProps) => {
const editorValue = templateData?.editorOptions?.value const editorValue = templateData?.editorOptions?.value
return ( return (
<ImageUploadEditorComponent <Editor
{...templateData} {...templateData}
column={column} column={column}
value={ value={
@ -577,7 +584,9 @@ const CardView = (props: CardViewProps) => {
}} }}
/> />
) )
}, []) },
[],
)
const renderImageViewerEditor = useCallback( const renderImageViewerEditor = useCallback(
(templateData: any) => ( (templateData: any) => (
@ -590,11 +599,22 @@ const CardView = (props: CardViewProps) => {
(templateOptions: any) => { (templateOptions: any) => {
const container = document.createElement('div') const container = document.createElement('div')
createRoot(container).render( createRoot(container).render(
renderImageUploadEditor(templateOptions?.model ?? templateOptions), renderValueEditor(templateOptions?.model ?? templateOptions, ImageUploadEditorComponent),
) )
return container return container
}, },
[renderImageUploadEditor], [renderValueEditor],
)
const ratingFormTemplate = useCallback(
(templateOptions: any) => {
const container = document.createElement('div')
createRoot(container).render(
renderValueEditor(templateOptions?.model ?? templateOptions, RatingEditorComponent),
)
return container
},
[renderValueEditor],
) )
const imageViewerFormTemplate = useCallback( const imageViewerFormTemplate = useCallback(
@ -893,10 +913,19 @@ const CardView = (props: CardViewProps) => {
? imageUploadFormTemplate ? imageUploadFormTemplate
: item.editorType2 === PlatformEditorTypes.dxImageViewer : item.editorType2 === PlatformEditorTypes.dxImageViewer
? imageViewerFormTemplate ? imageViewerFormTemplate
: item.editorType2 === PlatformEditorTypes.dxRating
? ratingFormTemplate
: undefined, : undefined,
} }
}, },
[gridDto, imageUploadFormTemplate, imageViewerFormTemplate, mode, translate], [
gridDto,
imageUploadFormTemplate,
imageViewerFormTemplate,
ratingFormTemplate,
mode,
translate,
],
) )
const editingFormItems = useMemo(() => { const editingFormItems = useMemo(() => {

View file

@ -37,7 +37,13 @@ import SubForms from '../form/SubForms'
import { RowMode } from '../form/types' import { RowMode } from '../form/types'
import { GridColumnData } from './GridColumnData' import { GridColumnData } from './GridColumnData'
import GridFilterDialogs from './GridFilterDialogs' import GridFilterDialogs from './GridFilterDialogs'
import { GridBoxEditorComponent, ImageUploadEditorComponent, ImageViewerEditorComponent, TagBoxEditorComponent } from './editors/cellEditors' import {
GridBoxEditorComponent,
ImageUploadEditorComponent,
ImageViewerEditorComponent,
RatingEditorComponent,
TagBoxEditorComponent,
} from './editors/cellEditors'
import { useFilters } from './useFilters' import { useFilters } from './useFilters'
import { updateWorkflowApprovalToolbarItems, useToolbar } from './useToolbar' import { updateWorkflowApprovalToolbarItems, useToolbar } from './useToolbar'
import { ImportDashboard } from '@/components/importManager/ImportDashboard' import { ImportDashboard } from '@/components/importManager/ImportDashboard'
@ -638,6 +644,10 @@ const Grid = (props: GridProps) => {
name="cellEditImageUpload" name="cellEditImageUpload"
render={(cell: any) => ImageUploadEditorComponent(adaptCustomEditorCell(cell))} render={(cell: any) => ImageUploadEditorComponent(adaptCustomEditorCell(cell))}
/> />
<Template
name="cellEditRating"
render={(cell: any) => RatingEditorComponent(adaptCustomEditorCell(cell))}
/>
<Template <Template
name="cellEditImageViewer" name="cellEditImageViewer"
render={(data: any) => ( render={(data: any) => (

View file

@ -31,7 +31,13 @@ import PageTitle from '@/components/shared/PageTitle'
import { RowMode } from '../form/types' import { RowMode } from '../form/types'
import { GridColumnData } from './GridColumnData' import { GridColumnData } from './GridColumnData'
import GridFilterDialogs from './GridFilterDialogs' import GridFilterDialogs from './GridFilterDialogs'
import { GridBoxEditorComponent, ImageUploadEditorComponent, ImageViewerEditorComponent, TagBoxEditorComponent } from './editors/cellEditors' import {
GridBoxEditorComponent,
ImageUploadEditorComponent,
ImageViewerEditorComponent,
RatingEditorComponent,
TagBoxEditorComponent,
} from './editors/cellEditors'
import { useFilters } from './useFilters' import { useFilters } from './useFilters'
import { updateWorkflowApprovalToolbarItems, useToolbar } from './useToolbar' import { updateWorkflowApprovalToolbarItems, useToolbar } from './useToolbar'
import WidgetGroup from '@/components/ui/Widget/WidgetGroup' import WidgetGroup from '@/components/ui/Widget/WidgetGroup'
@ -627,6 +633,10 @@ const Tree = (props: TreeProps) => {
name="cellEditImageUpload" name="cellEditImageUpload"
render={(cell: any) => ImageUploadEditorComponent(adaptCustomEditorCell(cell))} render={(cell: any) => ImageUploadEditorComponent(adaptCustomEditorCell(cell))}
/> />
<Template
name="cellEditRating"
render={(cell: any) => RatingEditorComponent(adaptCustomEditorCell(cell))}
/>
<Template <Template
name="cellEditImageViewer" name="cellEditImageViewer"
render={(data: any) => ( render={(data: any) => (

View file

@ -3,6 +3,7 @@ import type { ImageUploadOptionsDto } from '@/proxy/form/models'
import { GridBoxEditorComponent as FormGridBoxEditor } from '../../form/editors/GridBoxEditorComponent' import { GridBoxEditorComponent as FormGridBoxEditor } from '../../form/editors/GridBoxEditorComponent'
import { ImageUploadEditorComponent as FormImageUploadEditor } from '../../form/editors/ImageUploadEditorComponent' import { ImageUploadEditorComponent as FormImageUploadEditor } from '../../form/editors/ImageUploadEditorComponent'
import { ImageViewerEditorComponent as FormImageViewerEditor } from '../../form/editors/ImageViewerEditorComponent' import { ImageViewerEditorComponent as FormImageViewerEditor } from '../../form/editors/ImageViewerEditorComponent'
import { RatingEditorComponent as FormRatingEditor } from '../../form/editors/RatingEditorComponent'
import { TagBoxEditorComponent as FormTagBoxEditor } from '../../form/editors/TagBoxEditorComponent' import { TagBoxEditorComponent as FormTagBoxEditor } from '../../form/editors/TagBoxEditorComponent'
import { parseJsonObject } from '../shared/imageValue' import { parseJsonObject } from '../shared/imageValue'
@ -12,7 +13,6 @@ import { parseJsonObject } from '../shared/imageValue'
* `form/editors/*`tir; burada yalnızca hücre verisi bileşen prop'larına çevrilir. * `form/editors/*`tir; burada yalnızca hücre verisi bileşen prop'larına çevrilir.
*/ */
/** /**
* Ekranın editorOptions'ı + hücre bağlamının salt-okunur/devre dışı kararı. Bağlamın geri * Ekranın editorOptions'ı + hücre bağlamının salt-okunur/devre dışı kararı. Bağlamın geri
* kalanı (`value`, `onValueChanged`…) editörün kendi değer akışını ezeceği için alınmaz. * kalanı (`value`, `onValueChanged`…) editörün kendi değer akışını ezeceği için alınmaz.
@ -104,6 +104,15 @@ const ImageUploadEditorComponent = (cellElement: any): ReactElement => {
) )
} }
/** Liste düzenleme hücresi (Grid, Tree, CardView); editör form sayfasıyla aynı bileşendir. */
const RatingEditorComponent = (cellElement: any): ReactElement => (
<FormRatingEditor
value={cellElement.value}
editorOptions={getCellEditorOptions(cellElement)}
onValueChanged={(value) => cellElement.setValue(value)}
/>
)
const resolveTemplateValue = (templateData: any) => { const resolveTemplateValue = (templateData: any) => {
const dataField = const dataField =
templateData?.dataField || templateData?.dataField ||
@ -168,5 +177,6 @@ export {
GridBoxEditorComponent, GridBoxEditorComponent,
ImageUploadEditorComponent, ImageUploadEditorComponent,
ImageViewerEditorComponent, ImageViewerEditorComponent,
RatingEditorComponent,
TagBoxEditorComponent, TagBoxEditorComponent,
} }

View file

@ -468,6 +468,8 @@ const getDateEditorDefaults = (sourceDbType?: DbTypeEnum) => {
/** PlatformEditorTypes -> DevExtreme editorType eşlemesi. */ /** PlatformEditorTypes -> DevExtreme editorType eşlemesi. */
export const resolveEditorType = (editorType2?: EditorType2) => { export const resolveEditorType = (editorType2?: EditorType2) => {
if (editorType2 === PlatformEditorTypes.dxGridBox) return 'dxDropDownBox' if (editorType2 === PlatformEditorTypes.dxGridBox) return 'dxDropDownBox'
// Yıldız şablonu olmayan görünümlerde (Scheduler…) puan sayı olarak girilir.
if (editorType2 === PlatformEditorTypes.dxRating) return 'dxNumberBox'
if ( if (
editorType2 === PlatformEditorTypes.dxImageUpload || editorType2 === PlatformEditorTypes.dxImageUpload ||
editorType2 === PlatformEditorTypes.dxImageViewer editorType2 === PlatformEditorTypes.dxImageViewer

View file

@ -0,0 +1,35 @@
import { RATE_STAR_POINTS } from '@/components/ui/Rate/starShape'
const DEFAULT_RATING_COUNT = 5
/** Yıldız sayısı editorOptions.count'tan gelir; geçersizse 5. */
export const resolveRatingCount = (editorOptions?: { count?: unknown }) => {
const count = Number(editorOptions?.count)
return Number.isInteger(count) && count > 0 ? count : DEFAULT_RATING_COUNT
}
/** Kolon değeri (sayı ya da metin) -> 0..count arası tam puan; boş değer 0. */
export const normalizeRatingValue = (value: unknown, count: number) => {
const rating = Math.round(Number(value))
return Number.isFinite(rating) ? Math.min(Math.max(rating, 0), count) : 0
}
const STAR_FILLED_COLOR = '#fbbf24'
const STAR_EMPTY_COLOR = '#d1d5db'
/**
* Liste hücresi (Grid, Tree, Card) DOM ile çizilir; React kökü açmamak için yıldızlar
* `Rate` bileşeniyle aynı çokgenle SVG olarak yazılır.
*/
export const renderRatingCell = (cellElement: HTMLElement, value: unknown, count: number) => {
const rating = normalizeRatingValue(value, count)
const stars = Array.from({ length: count }, (_, index) => {
const color = index < rating ? STAR_FILLED_COLOR : STAR_EMPTY_COLOR
const fill = index < rating ? color : 'none'
return `<svg width="16" height="16" viewBox="0 0 24 24" style="display:inline-block;flex-shrink:0"><polygon points="${RATE_STAR_POINTS}" fill="${fill}" stroke="${color}" stroke-width="1.5" stroke-linejoin="round"/></svg>`
})
cellElement.style.cssText += 'display:flex;align-items:center;gap:2px;'
cellElement.innerHTML = stars.join('')
cellElement.title = rating ? `${rating} / ${count}` : ''
}

View file

@ -26,7 +26,8 @@ const showWorkflowMessages = (messages: string[] = []) => {
/** /**
* Liste formunda workflow onay alanı tanımlıysa eklenen kayıt için * Liste formunda workflow onay alanı tanımlıysa eklenen kayıt için
* workflow'u başlatır ve görünümü yeniler. * workflow'u başlatır ve görünümü yeniler. `approvalIsManualStart` açıksa kayıt
* taslak kalır; alt form satırları girildikten sonra akış başlat düğmesiyle başlar.
*/ */
export const startWorkflowForInsertedRow = ( export const startWorkflowForInsertedRow = (
event: any, event: any,
@ -34,7 +35,8 @@ export const startWorkflowForInsertedRow = (
listFormCode: string, listFormCode: string,
refresh: () => void, refresh: () => void,
) => { ) => {
if (!gridDto.gridOptions.workflowDto?.approvalStatusFieldName) return const workflowDto = gridDto.gridOptions.workflowDto
if (!workflowDto?.approvalStatusFieldName || workflowDto.approvalIsManualStart) return
const insertedKey = getPersistedInsertedKey(event, gridDto.gridOptions.keyFieldName) const insertedKey = getPersistedInsertedKey(event, gridDto.gridOptions.keyFieldName)
if (insertedKey === undefined) return if (insertedKey === undefined) return

View file

@ -32,6 +32,7 @@ import {
showImageHoverPreview, showImageHoverPreview,
thumbnailSource, thumbnailSource,
} from '../form/editors/imageHoverPreview' } from '../form/editors/imageHoverPreview'
import { renderRatingCell, resolveRatingCount } from './shared/ratingValue'
import { translateWorkflowLabel } from '@/utils/workflow/workflowHelpers' import { translateWorkflowLabel } from '@/utils/workflow/workflowHelpers'
import { findLookupColor, getLookupItemColor, tagBadgeColors } from '../shared/lookupColor' import { findLookupColor, getLookupItemColor, tagBadgeColors } from '../shared/lookupColor'
import { applyPhoneColumnFormat, needsPhoneCountries } from './shared/columnFormatters' import { applyPhoneColumnFormat, needsPhoneCountries } from './shared/columnFormatters'
@ -158,6 +159,14 @@ const cellTemplateImage = (
}) })
} }
const cellTemplateRating = (
cellElement: HTMLElement,
cellInfo: DataGridTypes.ColumnCellTemplateData<any, any>,
) => {
const column = cellInfo?.column as GridColumnData | undefined
renderRatingCell(cellElement, cellInfo?.value, resolveRatingCount(column?.extras?.editorOptions))
}
function calculateFilterExpressionMultiValue( function calculateFilterExpressionMultiValue(
this: DataGridTypes.Column, this: DataGridTypes.Column,
filterValue: any, filterValue: any,
@ -810,6 +819,16 @@ const useListFormColumns = ({
} }
// #endregion image upload editor // #endregion image upload editor
if (
colData.editorType2 === PlatformEditorTypes.dxRating &&
!colData.lookupDto?.dataSourceType
) {
column.extras = { multiValue: false, editorOptions }
column.editCellTemplate = 'cellEditRating'
column.cellTemplate = cellTemplateRating as any
column.alignment = 'left'
}
if (colData.validationRuleDto) { if (colData.validationRuleDto) {
// for server side validation : https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/DataValidation/jQuery/Light/ // for server side validation : https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/DataValidation/jQuery/Light/
column.validationRules = colData.validationRuleDto as ValidationRule[] column.validationRules = colData.validationRuleDto as ValidationRule[]