Devexpress Grid kısmına Rating komponenti eklendi
This commit is contained in:
parent
f341730b4a
commit
f44308756c
33 changed files with 496 additions and 238 deletions
16
.github/instructions/lowcode.instructions.md
vendored
16
.github/instructions/lowcode.instructions.md
vendored
|
|
@ -370,6 +370,7 @@ dörtlüyle üretilir (§9.8), görseldeki görünüm `DefaultLayout` olur.
|
|||
| Renk kutucuğu | `dxColorBox` | |
|
||||
| Kalın/italik araç çubuklu alan | `dxHtmlEditor` | `toolbar.items` |
|
||||
| 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` |
|
||||
| Gri, tıklanamaz alan | ilgili editör + `EditorOptions: {"readOnly": true}` | |
|
||||
| Kırmızı yıldızlı etiket | `IsRequired: true` | |
|
||||
|
|
@ -1072,12 +1073,12 @@ yazılır:
|
|||
| 25 | Xml | `xml` |
|
||||
| 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`,
|
||||
`dxDateRangeBox`, `dxDropDownBox`, `dxHtmlEditor`, `dxLookup`, `dxNumberBox`, `dxRadioGroup`,
|
||||
`dxRangeSlider`, `dxSelectBox`, `dxSlider`, `dxSwitch`, `dxTagBox`, `dxTextArea`, `dxTextBox`.
|
||||
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
|
||||
> (`"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` |
|
||||
| `dxSlider` | Tek değerli kaydırıcı | Sayı | `min`, `max`, `tooltip.showMode` |
|
||||
| `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. |
|
||||
| `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` |
|
||||
|
|
@ -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` |
|
||||
| Cascade temizleme/kilit | `list/shared/cascade.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
|
||||
çalışmaz; salt-okunur/devre dışı kararı, cascade ve `EditorScript` bu editörlere
|
||||
|
|
@ -1913,7 +1915,8 @@ cevaba yazılmaz.
|
|||
],
|
||||
"WorkflowDto": { "ApprovalUserFieldName": "", "ApprovalDateFieldName": "",
|
||||
"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ı |
|
||||
| `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 |
|
||||
| `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`**,
|
||||
**`Inform`**, **`End`**.
|
||||
|
|
@ -2627,7 +2631,7 @@ Builder'ı (§5) açar — çıktıları `EditorOptions`/`EditorScript` string'l
|
|||
**Adım 7 — İş Akışı.** Zorunlu ikili: `ApprovalUserFieldName` + `ApprovalStatusFieldName`;
|
||||
opsiyonel `ApprovalDateFieldName`, `ApprovalDescriptionFieldName`, `ApprovalIsFilterUserName`
|
||||
(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).
|
||||
|
||||
**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` |
|
||||
| GridBox | `columns`\*, `selectionMode`\*, `filterRowVisible`\* | `dxGridBox` |
|
||||
| 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` |
|
||||
| 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 |
|
||||
|
|
|
|||
|
|
@ -444,7 +444,7 @@ ve atanan kullanıcı alanlarından kurulur.
|
|||
- **Editör tipleri:** `dxTextBox`, `dxTextArea`, `dxNumberBox`, `dxDateBox`, `dxDateRangeBox`,
|
||||
`dxCalendar`, `dxCheckBox`, `dxSwitch`, `dxRadioGroup`, `dxSelectBox`, `dxTagBox`, `dxLookup`,
|
||||
`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.
|
||||
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`
|
||||
|
|
|
|||
|
|
@ -11,5 +11,11 @@ public class WorkflowDto
|
|||
public bool ApprovalIsFilterUserName { 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; } = [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1023,6 +1023,7 @@ public static class PlatformConsts
|
|||
public const string dxTextBox = "dxTextBox";
|
||||
public const string dxImageViewer = "dxImageViewer";
|
||||
public const string dxImageUpload = "dxImageUpload";
|
||||
public const string dxRating = "dxRating";
|
||||
}
|
||||
|
||||
public static class CustomEndpointConsts
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ public class Workflow : ValueObject
|
|||
public string ApprovalDescriptionFieldName { get; set; }
|
||||
public bool ApprovalIsFilterUserName { get; set; }
|
||||
public bool ApprovalIsResetWorkflow { get; set; }
|
||||
public bool ApprovalIsManualStart { get; set; }
|
||||
|
||||
protected override IEnumerable<object> GetAtomicValues()
|
||||
{
|
||||
|
|
@ -20,6 +21,7 @@ public class Workflow : ValueObject
|
|||
yield return ApprovalDescriptionFieldName;
|
||||
yield return ApprovalIsFilterUserName;
|
||||
yield return ApprovalIsResetWorkflow;
|
||||
yield return ApprovalIsManualStart;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
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
|
||||
(`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
|
||||
yetenek eklenirken ortak yardımcıya eklenir; tek tarafa yazılan yetenek eşliği bozar. Tablo:
|
||||
`lowcode.instructions.md` §4.3.4.
|
||||
|
|
|
|||
|
|
@ -45770,6 +45770,20 @@
|
|||
"Key": "App.ListFormEditWorkflow.ApprovalIsResetWorkflow",
|
||||
"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",
|
||||
"CultureName": "en",
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
|
|
@ -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 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `RequestNo` | Talep No | dxTextBox | | ✓ | |
|
||||
| `Priority` | Öncelik (0-3) | dxNumberBox | ✓ | ✓ | |
|
||||
| `Priority` | Öncelik (0-3) | dxRating | ✓ | ✓ | |
|
||||
| `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers · script |
|
||||
| `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department |
|
||||
| `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 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `RequestNo` | Talep No | dxTextBox | | ✓ | |
|
||||
| `Priority` | Öncelik (0-3) | dxNumberBox | ✓ | ✓ | |
|
||||
| `Priority` | Öncelik (0-3) | dxRating | ✓ | ✓ | |
|
||||
| `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers |
|
||||
| `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department |
|
||||
| `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 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `RequestNo` | Talep No | dxTextBox | | ✓ | |
|
||||
| `Priority` | Öncelik (0-3) | dxNumberBox | ✓ | ✓ | |
|
||||
| `Priority` | Öncelik (0-3) | dxRating | ✓ | ✓ | |
|
||||
| `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers |
|
||||
| `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department |
|
||||
| `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 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `RequestNo` | Talep No | dxTextBox | | ✓ | |
|
||||
| `Priority` | Öncelik (0-3) | dxNumberBox | ✓ | ✓ | |
|
||||
| `Priority` | Öncelik (0-3) | dxRating | ✓ | ✓ | |
|
||||
| `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers |
|
||||
| `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department |
|
||||
| `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 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `RequestNo` | Talep No | dxTextBox | | ✓ | |
|
||||
| `Priority` | Öncelik (0-3) | dxNumberBox | ✓ | ✓ | |
|
||||
| `Priority` | Öncelik (0-3) | dxRating | ✓ | ✓ | |
|
||||
| `RequesterUserId` | Talep Eden | dxSelectBox | ✓ | ✓ | sorgu: AbpUsers · script |
|
||||
| `DepartmentId` | Departman | dxSelectBox | ✓ | ✓ | sorgu: Hr_T_Department |
|
||||
| `WarehouseId` | Depo | dxSelectBox | ✓ | ✓ | sorgu: Inv_T_Warehouse · script |
|
||||
|
|
|
|||
|
|
@ -656,6 +656,7 @@
|
|||
"ApprovalDescriptionFieldName": "ApprovalNote",
|
||||
"ApprovalIsFilterUserName": false,
|
||||
"ApprovalIsResetWorkflow": true,
|
||||
"ApprovalIsManualStart": false,
|
||||
"Criteria": [
|
||||
{
|
||||
"ListFormCode": "App.Prc.MaterialRequest",
|
||||
|
|
@ -1385,7 +1386,7 @@
|
|||
"Format": null,
|
||||
"EditOrderNo": 2,
|
||||
"EditGroupOrderNo": 1,
|
||||
"EditorType2": "dxNumberBox",
|
||||
"EditorType2": "dxRating",
|
||||
"ColSpan": 1,
|
||||
"EditorOptions": "{\"min\":0,\"max\":3,\"showSpinButtons\":true}",
|
||||
"GridBoxOptions": null,
|
||||
|
|
|
|||
|
|
@ -509,8 +509,8 @@
|
|||
"Order": 1,
|
||||
"ItemType": "group",
|
||||
"Caption": "",
|
||||
"ColCount": 2,
|
||||
"ColSpan": 2
|
||||
"ColCount": 4,
|
||||
"ColSpan": 4
|
||||
}
|
||||
],
|
||||
"PermissionDto": {
|
||||
|
|
@ -575,6 +575,7 @@
|
|||
"ApprovalDescriptionFieldName": "ApprovalNote",
|
||||
"ApprovalIsFilterUserName": true,
|
||||
"ApprovalIsResetWorkflow": true,
|
||||
"ApprovalIsManualStart": false,
|
||||
"Criteria": [
|
||||
{
|
||||
"ListFormCode": "App.Prc.MaterialRequestDeptApproval",
|
||||
|
|
@ -1036,7 +1037,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": null,
|
||||
"EditGroupOrderNo": null,
|
||||
|
|
@ -1162,7 +1163,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 1,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -1300,11 +1301,11 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 2,
|
||||
"EditGroupOrderNo": 1,
|
||||
"EditorType2": "dxNumberBox",
|
||||
"EditorType2": "dxRating",
|
||||
"ColSpan": 1,
|
||||
"EditorOptions": "{\"readOnly\":true}",
|
||||
"GridBoxOptions": null,
|
||||
|
|
@ -1438,7 +1439,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 3,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -1576,7 +1577,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 4,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -1714,7 +1715,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 5,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -1852,7 +1853,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 6,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -1990,7 +1991,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 7,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -2128,7 +2129,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 8,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -2254,7 +2255,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 9,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -2380,12 +2381,12 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 10,
|
||||
"EditGroupOrderNo": 1,
|
||||
"EditorType2": "dxSelectBox",
|
||||
"ColSpan": 1,
|
||||
"ColSpan": 3,
|
||||
"EditorOptions": "{\"showClearButton\":true}",
|
||||
"GridBoxOptions": null,
|
||||
"TagBoxOptions": null,
|
||||
|
|
@ -2506,12 +2507,12 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 11,
|
||||
"EditGroupOrderNo": 1,
|
||||
"EditorType2": "dxTextArea",
|
||||
"ColSpan": 2,
|
||||
"ColSpan": 4,
|
||||
"EditorOptions": "{\"readOnly\":true}",
|
||||
"GridBoxOptions": null,
|
||||
"TagBoxOptions": null,
|
||||
|
|
@ -2632,7 +2633,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": null,
|
||||
"EditGroupOrderNo": null,
|
||||
|
|
@ -2758,7 +2759,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": null,
|
||||
"EditGroupOrderNo": null,
|
||||
|
|
@ -2884,7 +2885,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": null,
|
||||
"EditGroupOrderNo": null,
|
||||
|
|
@ -3010,7 +3011,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": null,
|
||||
"EditGroupOrderNo": null,
|
||||
|
|
|
|||
|
|
@ -586,6 +586,7 @@
|
|||
"ApprovalDescriptionFieldName": null,
|
||||
"ApprovalIsFilterUserName": false,
|
||||
"ApprovalIsResetWorkflow": false,
|
||||
"ApprovalIsManualStart": false,
|
||||
"Criteria": []
|
||||
},
|
||||
"UserId": null,
|
||||
|
|
@ -1224,7 +1225,7 @@
|
|||
"Format": null,
|
||||
"EditOrderNo": 2,
|
||||
"EditGroupOrderNo": 1,
|
||||
"EditorType2": "dxNumberBox",
|
||||
"EditorType2": "dxRating",
|
||||
"ColSpan": 1,
|
||||
"EditorOptions": "{\"readOnly\":true}",
|
||||
"GridBoxOptions": null,
|
||||
|
|
|
|||
|
|
@ -604,6 +604,7 @@
|
|||
"ApprovalDescriptionFieldName": "ApprovalNote",
|
||||
"ApprovalIsFilterUserName": true,
|
||||
"ApprovalIsResetWorkflow": true,
|
||||
"ApprovalIsManualStart": false,
|
||||
"Criteria": [
|
||||
{
|
||||
"ListFormCode": "App.Prc.MaterialRequestPurchaseApproval",
|
||||
|
|
@ -1065,7 +1066,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": null,
|
||||
"EditGroupOrderNo": null,
|
||||
|
|
@ -1191,7 +1192,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 1,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -1329,11 +1330,11 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 2,
|
||||
"EditGroupOrderNo": 1,
|
||||
"EditorType2": "dxNumberBox",
|
||||
"EditorType2": "dxRating",
|
||||
"ColSpan": 1,
|
||||
"EditorOptions": "{\"readOnly\":true}",
|
||||
"GridBoxOptions": null,
|
||||
|
|
@ -1467,7 +1468,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 3,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -1605,7 +1606,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 4,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -1743,7 +1744,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 5,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -1881,7 +1882,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 6,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -2019,7 +2020,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 7,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -2157,7 +2158,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 8,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -2283,7 +2284,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 9,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -2409,7 +2410,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 10,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -2535,7 +2536,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 11,
|
||||
"EditGroupOrderNo": 1,
|
||||
|
|
@ -2661,7 +2662,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": null,
|
||||
"EditGroupOrderNo": null,
|
||||
|
|
@ -2787,7 +2788,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": null,
|
||||
"EditGroupOrderNo": null,
|
||||
|
|
@ -2913,7 +2914,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": null,
|
||||
"EditGroupOrderNo": null,
|
||||
|
|
@ -3039,7 +3040,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": null,
|
||||
"EditGroupOrderNo": null,
|
||||
|
|
@ -3165,7 +3166,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 1,
|
||||
"EditGroupOrderNo": 2,
|
||||
|
|
@ -3291,7 +3292,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 2,
|
||||
"EditGroupOrderNo": 2,
|
||||
|
|
@ -3417,7 +3418,7 @@
|
|||
"GroupInterval": "",
|
||||
"WordWrapEnabled": false
|
||||
},
|
||||
"Alignment": null,
|
||||
"Alignment": "left",
|
||||
"Format": null,
|
||||
"EditOrderNo": 3,
|
||||
"EditGroupOrderNo": 2,
|
||||
|
|
|
|||
|
|
@ -670,12 +670,13 @@
|
|||
"DefaultLayout": "grid"
|
||||
},
|
||||
"WorkflowDto": {
|
||||
"ApprovalUserFieldName": null,
|
||||
"ApprovalUserFieldName": "ApproverId",
|
||||
"ApprovalDateFieldName": "ApprovalDate",
|
||||
"ApprovalStatusFieldName": "ApprovalStatus",
|
||||
"ApprovalDescriptionFieldName": "ApprovalNote",
|
||||
"ApprovalIsFilterUserName": false,
|
||||
"ApprovalIsResetWorkflow": true,
|
||||
"ApprovalIsManualStart": false,
|
||||
"Criteria": [
|
||||
{
|
||||
"ListFormCode": "App.Prc.MyMaterialRequest",
|
||||
|
|
@ -1405,7 +1406,7 @@
|
|||
"Format": null,
|
||||
"EditOrderNo": 2,
|
||||
"EditGroupOrderNo": 1,
|
||||
"EditorType2": "dxNumberBox",
|
||||
"EditorType2": "dxRating",
|
||||
"ColSpan": 1,
|
||||
"EditorOptions": "{\"min\":0,\"max\":3,\"showSpinButtons\":true}",
|
||||
"GridBoxOptions": null,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,9 @@
|
|||
import React, {
|
||||
forwardRef,
|
||||
useState,
|
||||
useCallback,
|
||||
useRef,
|
||||
type KeyboardEvent,
|
||||
type ReactNode,
|
||||
} from 'react'
|
||||
import React, { forwardRef, useState, useCallback, type KeyboardEvent, type ReactNode } from 'react'
|
||||
import classNames from 'classnames'
|
||||
import { useConfig } from '../ConfigProvider'
|
||||
import type { CommonProps } from '../@types/common'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { RATE_STAR_POINTS } from './starShape'
|
||||
|
||||
export interface RateProps extends CommonProps {
|
||||
/** Toplam yıldız sayısı. Varsayılan: 5 */
|
||||
|
|
@ -59,7 +53,7 @@ const StarIcon = ({ filled, half, size }: { filled: boolean; half: boolean; size
|
|||
</defs>
|
||||
)}
|
||||
<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'}
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
|
|
@ -95,7 +89,6 @@ const Rate = forwardRef<HTMLDivElement, RateProps>((props, ref) => {
|
|||
const isControlled = valueProp !== undefined
|
||||
const [internalValue, setInternalValue] = useState(defaultValue)
|
||||
const [hoverValue, setHoverValue] = useState<number | null>(null)
|
||||
const lastClickedRef = useRef<number | null>(null)
|
||||
|
||||
const value = isControlled ? valueProp! : internalValue
|
||||
const displayValue = hoverValue !== null ? hoverValue : value
|
||||
|
|
@ -139,18 +132,14 @@ const Rate = forwardRef<HTMLDivElement, RateProps>((props, ref) => {
|
|||
const offsetX = e.clientX - rect.left
|
||||
const clickedVal = getStarValue(index, offsetX, rect.width)
|
||||
|
||||
let newVal = clickedVal
|
||||
if (allowClear && clickedVal === lastClickedRef.current) {
|
||||
newVal = 0
|
||||
lastClickedRef.current = null
|
||||
} else {
|
||||
lastClickedRef.current = clickedVal
|
||||
}
|
||||
// Seçili yıldıza yeniden tıklamak değeri sıfırlar; karşılaştırma son tıklamayla değil
|
||||
// güncel değerle yapılır, yoksa kontrollü kullanımda dışarıdan gelen değer temizlenemez.
|
||||
const newVal = allowClear && clickedVal === value ? 0 : clickedVal
|
||||
|
||||
if (!isControlled) setInternalValue(newVal)
|
||||
onChange?.(newVal)
|
||||
},
|
||||
[disabled, readOnly, allowClear, allowHalf, isControlled, onChange],
|
||||
[disabled, readOnly, allowClear, allowHalf, value, isControlled, onChange],
|
||||
)
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
|
|
|
|||
3
ui/src/components/ui/Rate/starShape.ts
Normal file
3
ui/src/components/ui/Rate/starShape.ts
Normal 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'
|
||||
|
|
@ -871,6 +871,7 @@ export enum PlatformEditorTypes {
|
|||
dxGridBox = 'dxGridBox',
|
||||
dxImageViewer = 'dxImageViewer',
|
||||
dxImageUpload = 'dxImageUpload',
|
||||
dxRating = 'dxRating',
|
||||
}
|
||||
|
||||
export enum GanttScaleTypeEnum {
|
||||
|
|
@ -948,6 +949,7 @@ export interface WorkflowDto {
|
|||
approvalDescriptionFieldName: string
|
||||
approvalIsFilterUserName: boolean
|
||||
approvalIsResetWorkflow: boolean
|
||||
approvalIsManualStart: boolean
|
||||
criteria: ListFormWorkflowCriteriaDto[]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -319,14 +319,25 @@ export function FormTabWorkflow(
|
|||
approvalDescriptionFieldName: string(),
|
||||
approvalIsFilterUserName: bool(),
|
||||
approvalIsResetWorkflow: bool(),
|
||||
approvalIsManualStart: bool(),
|
||||
}),
|
||||
})
|
||||
|
||||
const initialValues = useStoreState((s) => s.admin.lists.values)
|
||||
if (!initialValues) {
|
||||
const listValues = useStoreState((s) => s.admin.lists.values)
|
||||
if (!listValues) {
|
||||
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 (
|
||||
<div>
|
||||
<Formik
|
||||
|
|
@ -341,7 +352,7 @@ export function FormTabWorkflow(
|
|||
<Form>
|
||||
<FormContainer size="sm">
|
||||
<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
|
||||
asterisk
|
||||
label={translate('::App.ListFormEditWorkflow.ApprovalUserFieldName')}
|
||||
|
|
@ -468,6 +479,19 @@ export function FormTabWorkflow(
|
|||
>
|
||||
<Field name="workflowDto.approvalIsResetWorkflow" component={Checkbox} />
|
||||
</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>
|
||||
|
||||
<Button block size="sm" variant="solid" type="submit" loading={isSubmitting}>
|
||||
|
|
|
|||
|
|
@ -448,7 +448,8 @@ function FormFields({
|
|||
<Container className="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="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">
|
||||
<Segment
|
||||
size="xs"
|
||||
|
|
@ -469,99 +470,101 @@ function FormFields({
|
|||
</Segment.Item>
|
||||
</Segment>
|
||||
</div>
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={onDragEnd}
|
||||
>
|
||||
<SortableContext
|
||||
items={orderedFields.map((f) => f.id!)}
|
||||
strategy={verticalListSortingStrategy}
|
||||
<div className="flex-1 min-h-0 overflow-y-auto">
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={onDragEnd}
|
||||
>
|
||||
<Table compact>
|
||||
<THead>
|
||||
<Tr>
|
||||
<Th className="w-3/12">
|
||||
<div>
|
||||
{canCreate && (
|
||||
<Button
|
||||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={() => {
|
||||
setIsNewField(true)
|
||||
setRow({} as ColumnFormatEditDto)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{canCreate && (
|
||||
<Button
|
||||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="sm"
|
||||
title="Copy"
|
||||
icon={<FaCopy />}
|
||||
onClick={async (e) => {
|
||||
e.preventDefault()
|
||||
setIsCopyField(true)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{canCreate && allDbColumns.length > 0 && (
|
||||
<Button
|
||||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="sm"
|
||||
title={translate('::App.ListFormField.AddAllColumns')}
|
||||
icon={<FaTable />}
|
||||
loading={isAddingAllColumns}
|
||||
onClick={addAllColumns}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Th>
|
||||
<Th className="w-3/12">Field</Th>
|
||||
<Th className="w-2/12">Culture</Th>
|
||||
<Th className="hidden sm:table-cell w-2/12">Role</Th>
|
||||
<Th className="hidden sm:table-cell w-2/12">User</Th>
|
||||
</Tr>
|
||||
</THead>
|
||||
<TBody>
|
||||
{orderedFields.map((field) => (
|
||||
<SortableFieldRow
|
||||
key={field.id}
|
||||
field={field}
|
||||
isSelected={field.id === row?.id}
|
||||
canDelete={canDelete}
|
||||
dragDisabled={
|
||||
!canUpdate ||
|
||||
isSavingOrder ||
|
||||
(orderMode === 'edit' && field.editOrderNo == null)
|
||||
}
|
||||
dragTitle={translate('::App.ListFormField.DragToReorder')}
|
||||
roleLabel={
|
||||
field.roleId
|
||||
? (roleList.find((a) => a.value === field.roleId)?.label ?? 'X')
|
||||
: undefined
|
||||
}
|
||||
userLabel={
|
||||
field.userId
|
||||
? (userList.find((a) => a.value === field.userId)?.label ?? 'X')
|
||||
: undefined
|
||||
}
|
||||
onSelect={() => setRow(field)}
|
||||
onDelete={() => field.id && setDeleteRowId(field.id)}
|
||||
/>
|
||||
))}
|
||||
</TBody>
|
||||
</Table>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
<SortableContext
|
||||
items={orderedFields.map((f) => f.id!)}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
<Table compact>
|
||||
<THead className="sticky top-0 z-10">
|
||||
<Tr>
|
||||
<Th className="w-3/12">
|
||||
<div>
|
||||
{canCreate && (
|
||||
<Button
|
||||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={() => {
|
||||
setIsNewField(true)
|
||||
setRow({} as ColumnFormatEditDto)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{canCreate && (
|
||||
<Button
|
||||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="sm"
|
||||
title="Copy"
|
||||
icon={<FaCopy />}
|
||||
onClick={async (e) => {
|
||||
e.preventDefault()
|
||||
setIsCopyField(true)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{canCreate && allDbColumns.length > 0 && (
|
||||
<Button
|
||||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="sm"
|
||||
title={translate('::App.ListFormField.AddAllColumns')}
|
||||
icon={<FaTable />}
|
||||
loading={isAddingAllColumns}
|
||||
onClick={addAllColumns}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Th>
|
||||
<Th className="w-3/12">Field</Th>
|
||||
<Th className="w-2/12">Culture</Th>
|
||||
<Th className="hidden sm:table-cell w-2/12">Role</Th>
|
||||
<Th className="hidden sm:table-cell w-2/12">User</Th>
|
||||
</Tr>
|
||||
</THead>
|
||||
<TBody>
|
||||
{orderedFields.map((field) => (
|
||||
<SortableFieldRow
|
||||
key={field.id}
|
||||
field={field}
|
||||
isSelected={field.id === row?.id}
|
||||
canDelete={canDelete}
|
||||
dragDisabled={
|
||||
!canUpdate ||
|
||||
isSavingOrder ||
|
||||
(orderMode === 'edit' && field.editOrderNo == null)
|
||||
}
|
||||
dragTitle={translate('::App.ListFormField.DragToReorder')}
|
||||
roleLabel={
|
||||
field.roleId
|
||||
? (roleList.find((a) => a.value === field.roleId)?.label ?? 'X')
|
||||
: undefined
|
||||
}
|
||||
userLabel={
|
||||
field.userId
|
||||
? (userList.find((a) => a.value === field.userId)?.label ?? 'X')
|
||||
: undefined
|
||||
}
|
||||
onSelect={() => setRow(field)}
|
||||
onDelete={() => field.id && setDeleteRowId(field.id)}
|
||||
/>
|
||||
))}
|
||||
</TBody>
|
||||
</Table>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
</div>
|
||||
</div>
|
||||
<div className="2xl:col-span-3 lg:col-span-3 xl:col-span-2">
|
||||
<FormFieldEdit
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ const DROPDOWN_EDITORS = [
|
|||
const DATE_EDITORS = ['dxDateBox', 'dxCalendar', 'dxDateRangeBox']
|
||||
const IMAGE_EDITORS = ['dxImageUpload', 'dxImageViewer']
|
||||
const SLIDER_EDITORS = ['dxSlider', 'dxRangeSlider']
|
||||
const RATING_EDITORS = ['dxRating']
|
||||
|
||||
/* ------------------------------------------------------------------ *
|
||||
* Ayar sözlüğü
|
||||
|
|
@ -855,6 +856,14 @@ export const optionSpecs: OptionSpec[] = [
|
|||
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ı ────────────────────────────────────────────────────
|
||||
{ path: 'min', label: 'min', type: 'number', group: 'slider', editors: SLIDER_EDITORS },
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ export const columnEditorTypeListOptions = [
|
|||
{ value: PlatformEditorTypes.dxGridBox, label: PlatformEditorTypes.dxGridBox },
|
||||
{ value: PlatformEditorTypes.dxImageViewer, label: PlatformEditorTypes.dxImageViewer },
|
||||
{ value: PlatformEditorTypes.dxImageUpload, label: PlatformEditorTypes.dxImageUpload },
|
||||
{ value: PlatformEditorTypes.dxRating, label: PlatformEditorTypes.dxRating },
|
||||
{ value: 'dxHtmlEditor', label: 'dxHtmlEditor' },
|
||||
{ value: 'dxLookup', label: 'dxLookup' },
|
||||
{ value: 'dxNumberBox', label: 'dxNumberBox' },
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ const initialValues: ListFormWizardDto = {
|
|||
approvalDescriptionFieldName: '',
|
||||
approvalIsFilterUserName: false,
|
||||
approvalIsResetWorkflow: false,
|
||||
approvalIsManualStart: false,
|
||||
criteria: [],
|
||||
},
|
||||
menuEn: '',
|
||||
|
|
@ -445,6 +446,7 @@ const Wizard = () => {
|
|||
approvalDescriptionFieldName: '',
|
||||
approvalIsFilterUserName: false,
|
||||
approvalIsResetWorkflow: false,
|
||||
approvalIsManualStart: false,
|
||||
criteria: [],
|
||||
})
|
||||
const [criteria, setCriteria] = useState<ListFormWorkflowCriteriaDto[]>([])
|
||||
|
|
|
|||
|
|
@ -391,7 +391,7 @@ function WizardStepWorkflow({
|
|||
<Form>
|
||||
<Card className="mb-4">
|
||||
<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'],
|
||||
[
|
||||
|
|
@ -484,6 +484,26 @@ function WizardStepWorkflow({
|
|||
)}
|
||||
</Field>
|
||||
</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>
|
||||
</FormContainer>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { Dispatch, ReactNode, RefObject, useCallback, useEffect, useRef, useStat
|
|||
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
|
||||
import { ImageUploadEditorComponent } from './editors/ImageUploadEditorComponent'
|
||||
import { ImageViewerEditorComponent } from './editors/ImageViewerEditorComponent'
|
||||
import { RatingEditorComponent } from './editors/RatingEditorComponent'
|
||||
import { TagBoxEditorComponent } from './editors/TagBoxEditorComponent'
|
||||
import { RowMode, SimpleItemWithColData } from './types'
|
||||
import { PlatformEditorTypes } from '@/proxy/form/models'
|
||||
|
|
@ -47,6 +48,7 @@ const CUSTOM_EDITOR_TYPES = [
|
|||
PlatformEditorTypes.dxGridBox,
|
||||
PlatformEditorTypes.dxImageUpload,
|
||||
PlatformEditorTypes.dxImageViewer,
|
||||
PlatformEditorTypes.dxRating,
|
||||
] as const
|
||||
|
||||
const FormDevExpress = (props: {
|
||||
|
|
@ -402,6 +404,14 @@ const FormDevExpress = (props: {
|
|||
editorOptions={editorOptions}
|
||||
/>
|
||||
)
|
||||
case PlatformEditorTypes.dxRating:
|
||||
return (
|
||||
<RatingEditorComponent
|
||||
value={value}
|
||||
onValueChanged={onChange}
|
||||
editorOptions={editorOptions}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
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.
|
||||
const isImageEditor =
|
||||
const isStandaloneEditor =
|
||||
formItem.editorType2 === PlatformEditorTypes.dxImageUpload ||
|
||||
formItem.editorType2 === PlatformEditorTypes.dxImageViewer
|
||||
const itemProps = isImageEditor
|
||||
formItem.editorType2 === PlatformEditorTypes.dxImageViewer ||
|
||||
formItem.editorType2 === PlatformEditorTypes.dxRating
|
||||
const itemProps = isStandaloneEditor
|
||||
? {
|
||||
dataField: formItem.dataField,
|
||||
name: formItem.name,
|
||||
|
|
|
|||
44
ui/src/views/form/editors/RatingEditorComponent.tsx
Normal file
44
ui/src/views/form/editors/RatingEditorComponent.tsx
Normal 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 }
|
||||
|
|
@ -14,6 +14,7 @@ export type EditorType2 =
|
|||
| PlatformEditorTypes.dxGridBox
|
||||
| PlatformEditorTypes.dxImageViewer
|
||||
| PlatformEditorTypes.dxImageUpload
|
||||
| PlatformEditorTypes.dxRating
|
||||
export type SimpleItemWithColData = Overwrite<
|
||||
SimpleItem,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -34,14 +34,18 @@ import DxCardView, {
|
|||
} from 'devextreme-react/card-view'
|
||||
import CustomStore from 'devextreme/data/custom_store'
|
||||
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 PageTitle from '@/components/shared/PageTitle'
|
||||
import { NotePanel } from '../form/notes/NotePanel'
|
||||
import SubForms from '../form/SubForms'
|
||||
import GridFilterDialogs from './GridFilterDialogs'
|
||||
import { GridExtraFilterToolbar } from './GridExtraFilterToolbar'
|
||||
import { ImageUploadEditorComponent, ImageViewerEditorComponent } from './editors/cellEditors'
|
||||
import {
|
||||
ImageUploadEditorComponent,
|
||||
ImageViewerEditorComponent,
|
||||
RatingEditorComponent,
|
||||
} from './editors/cellEditors'
|
||||
import {
|
||||
autoNumber,
|
||||
extractSearchParamsFields,
|
||||
|
|
@ -93,9 +97,11 @@ const toCardColumn = (
|
|||
const dataField = column.dataField
|
||||
const originalCustomizeText =
|
||||
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' &&
|
||||
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'
|
||||
|
||||
return Object.fromEntries(
|
||||
|
|
@ -129,27 +135,26 @@ const toCardColumn = (
|
|||
dataType: column.dataType,
|
||||
editorOptions: column.editorOptions,
|
||||
filterValue: column.filterValue,
|
||||
fieldValueTemplate:
|
||||
isImageColumn && typeof column.cellTemplate === 'function'
|
||||
fieldValueTemplate: hasCellTemplate
|
||||
? (templateOptions: any) => {
|
||||
const templateData = templateOptions?.model ?? templateOptions
|
||||
const container = document.createElement('div')
|
||||
column.cellTemplate(container, {
|
||||
column,
|
||||
data: templateData.field?.card?.data,
|
||||
displayValue: templateData.field?.displayValue,
|
||||
value: templateData.field?.value,
|
||||
})
|
||||
return container
|
||||
}
|
||||
: isHtmlColumn
|
||||
? (templateOptions: any) => {
|
||||
const templateData = templateOptions?.model ?? templateOptions
|
||||
const container = document.createElement('div')
|
||||
column.cellTemplate(container, {
|
||||
column,
|
||||
data: templateData.field?.card?.data,
|
||||
displayValue: templateData.field?.displayValue,
|
||||
value: templateData.field?.value,
|
||||
})
|
||||
container.innerHTML = String(templateData.field?.value ?? '')
|
||||
return container
|
||||
}
|
||||
: isHtmlColumn
|
||||
? (templateOptions: any) => {
|
||||
const templateData = templateOptions?.model ?? templateOptions
|
||||
const container = document.createElement('div')
|
||||
container.innerHTML = String(templateData.field?.value ?? '')
|
||||
return container
|
||||
}
|
||||
: undefined,
|
||||
: undefined,
|
||||
format: column.format,
|
||||
headerFilter: column.headerFilter,
|
||||
lookup: column.lookup,
|
||||
|
|
@ -545,39 +550,43 @@ const CardView = (props: CardViewProps) => {
|
|||
const commandButtonsRef = useRef(commandButtons)
|
||||
commandButtonsRef.current = commandButtons
|
||||
|
||||
const renderImageUploadEditor = useCallback((templateData: any) => {
|
||||
const configuredDataField =
|
||||
templateData?.dataField ??
|
||||
templateData?.item?.dataField ??
|
||||
templateData?.editorOptions?.name ??
|
||||
templateData?.name
|
||||
const dataField = getEditingFieldName(configuredDataField)
|
||||
const column = listColumnsCacheRef.current.columns.find(
|
||||
(candidate) => candidate.dataField?.toLowerCase() === String(dataField ?? '').toLowerCase(),
|
||||
)
|
||||
const component = templateData?.component
|
||||
const currentFormData = component?.option?.('formData') ?? formDataRef.current ?? {}
|
||||
const editorValue = templateData?.editorOptions?.value
|
||||
/** 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 =
|
||||
templateData?.dataField ??
|
||||
templateData?.item?.dataField ??
|
||||
templateData?.editorOptions?.name ??
|
||||
templateData?.name
|
||||
const dataField = getEditingFieldName(configuredDataField)
|
||||
const column = listColumnsCacheRef.current.columns.find(
|
||||
(candidate) => candidate.dataField?.toLowerCase() === String(dataField ?? '').toLowerCase(),
|
||||
)
|
||||
const component = templateData?.component
|
||||
const currentFormData = component?.option?.('formData') ?? formDataRef.current ?? {}
|
||||
const editorValue = templateData?.editorOptions?.value
|
||||
|
||||
return (
|
||||
<ImageUploadEditorComponent
|
||||
{...templateData}
|
||||
column={column}
|
||||
value={
|
||||
editorValue !== undefined ? editorValue : getValueByField(currentFormData, dataField)
|
||||
}
|
||||
setValue={(value: any) => {
|
||||
if (!dataField) return
|
||||
if (typeof templateData?.editorOptions?.onValueChanged === 'function') {
|
||||
templateData.editorOptions.onValueChanged({ value })
|
||||
} else {
|
||||
component?.updateData?.(dataField, value)
|
||||
return (
|
||||
<Editor
|
||||
{...templateData}
|
||||
column={column}
|
||||
value={
|
||||
editorValue !== undefined ? editorValue : getValueByField(currentFormData, dataField)
|
||||
}
|
||||
formDataRef.current = { ...currentFormData, [dataField]: value }
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}, [])
|
||||
setValue={(value: any) => {
|
||||
if (!dataField) return
|
||||
if (typeof templateData?.editorOptions?.onValueChanged === 'function') {
|
||||
templateData.editorOptions.onValueChanged({ value })
|
||||
} else {
|
||||
component?.updateData?.(dataField, value)
|
||||
}
|
||||
formDataRef.current = { ...currentFormData, [dataField]: value }
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
||||
const renderImageViewerEditor = useCallback(
|
||||
(templateData: any) => (
|
||||
|
|
@ -590,11 +599,22 @@ const CardView = (props: CardViewProps) => {
|
|||
(templateOptions: any) => {
|
||||
const container = document.createElement('div')
|
||||
createRoot(container).render(
|
||||
renderImageUploadEditor(templateOptions?.model ?? templateOptions),
|
||||
renderValueEditor(templateOptions?.model ?? templateOptions, ImageUploadEditorComponent),
|
||||
)
|
||||
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(
|
||||
|
|
@ -893,10 +913,19 @@ const CardView = (props: CardViewProps) => {
|
|||
? imageUploadFormTemplate
|
||||
: item.editorType2 === PlatformEditorTypes.dxImageViewer
|
||||
? imageViewerFormTemplate
|
||||
: undefined,
|
||||
: item.editorType2 === PlatformEditorTypes.dxRating
|
||||
? ratingFormTemplate
|
||||
: undefined,
|
||||
}
|
||||
},
|
||||
[gridDto, imageUploadFormTemplate, imageViewerFormTemplate, mode, translate],
|
||||
[
|
||||
gridDto,
|
||||
imageUploadFormTemplate,
|
||||
imageViewerFormTemplate,
|
||||
ratingFormTemplate,
|
||||
mode,
|
||||
translate,
|
||||
],
|
||||
)
|
||||
|
||||
const editingFormItems = useMemo(() => {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,13 @@ import SubForms from '../form/SubForms'
|
|||
import { RowMode } from '../form/types'
|
||||
import { GridColumnData } from './GridColumnData'
|
||||
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 { updateWorkflowApprovalToolbarItems, useToolbar } from './useToolbar'
|
||||
import { ImportDashboard } from '@/components/importManager/ImportDashboard'
|
||||
|
|
@ -638,6 +644,10 @@ const Grid = (props: GridProps) => {
|
|||
name="cellEditImageUpload"
|
||||
render={(cell: any) => ImageUploadEditorComponent(adaptCustomEditorCell(cell))}
|
||||
/>
|
||||
<Template
|
||||
name="cellEditRating"
|
||||
render={(cell: any) => RatingEditorComponent(adaptCustomEditorCell(cell))}
|
||||
/>
|
||||
<Template
|
||||
name="cellEditImageViewer"
|
||||
render={(data: any) => (
|
||||
|
|
|
|||
|
|
@ -31,7 +31,13 @@ import PageTitle from '@/components/shared/PageTitle'
|
|||
import { RowMode } from '../form/types'
|
||||
import { GridColumnData } from './GridColumnData'
|
||||
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 { updateWorkflowApprovalToolbarItems, useToolbar } from './useToolbar'
|
||||
import WidgetGroup from '@/components/ui/Widget/WidgetGroup'
|
||||
|
|
@ -627,6 +633,10 @@ const Tree = (props: TreeProps) => {
|
|||
name="cellEditImageUpload"
|
||||
render={(cell: any) => ImageUploadEditorComponent(adaptCustomEditorCell(cell))}
|
||||
/>
|
||||
<Template
|
||||
name="cellEditRating"
|
||||
render={(cell: any) => RatingEditorComponent(adaptCustomEditorCell(cell))}
|
||||
/>
|
||||
<Template
|
||||
name="cellEditImageViewer"
|
||||
render={(data: any) => (
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import type { ImageUploadOptionsDto } from '@/proxy/form/models'
|
|||
import { GridBoxEditorComponent as FormGridBoxEditor } from '../../form/editors/GridBoxEditorComponent'
|
||||
import { ImageUploadEditorComponent as FormImageUploadEditor } from '../../form/editors/ImageUploadEditorComponent'
|
||||
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 { 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.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
|
@ -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 dataField =
|
||||
templateData?.dataField ||
|
||||
|
|
@ -168,5 +177,6 @@ export {
|
|||
GridBoxEditorComponent,
|
||||
ImageUploadEditorComponent,
|
||||
ImageViewerEditorComponent,
|
||||
RatingEditorComponent,
|
||||
TagBoxEditorComponent,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -468,6 +468,8 @@ const getDateEditorDefaults = (sourceDbType?: DbTypeEnum) => {
|
|||
/** PlatformEditorTypes -> DevExtreme editorType eşlemesi. */
|
||||
export const resolveEditorType = (editorType2?: EditorType2) => {
|
||||
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 (
|
||||
editorType2 === PlatformEditorTypes.dxImageUpload ||
|
||||
editorType2 === PlatformEditorTypes.dxImageViewer
|
||||
|
|
|
|||
35
ui/src/views/list/shared/ratingValue.ts
Normal file
35
ui/src/views/list/shared/ratingValue.ts
Normal 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}` : ''
|
||||
}
|
||||
|
|
@ -26,7 +26,8 @@ const showWorkflowMessages = (messages: string[] = []) => {
|
|||
|
||||
/**
|
||||
* 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 = (
|
||||
event: any,
|
||||
|
|
@ -34,7 +35,8 @@ export const startWorkflowForInsertedRow = (
|
|||
listFormCode: string,
|
||||
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)
|
||||
if (insertedKey === undefined) return
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import {
|
|||
showImageHoverPreview,
|
||||
thumbnailSource,
|
||||
} from '../form/editors/imageHoverPreview'
|
||||
import { renderRatingCell, resolveRatingCount } from './shared/ratingValue'
|
||||
import { translateWorkflowLabel } from '@/utils/workflow/workflowHelpers'
|
||||
import { findLookupColor, getLookupItemColor, tagBadgeColors } from '../shared/lookupColor'
|
||||
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(
|
||||
this: DataGridTypes.Column,
|
||||
filterValue: any,
|
||||
|
|
@ -810,6 +819,16 @@ const useListFormColumns = ({
|
|||
}
|
||||
// #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) {
|
||||
// for server side validation : https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/DataValidation/jQuery/Light/
|
||||
column.validationRules = colData.validationRuleDto as ValidationRule[]
|
||||
|
|
|
|||
Loading…
Reference in a new issue