FormDevExpress Buton yapısı değiştirildi
This commit is contained in:
parent
59ec8950a3
commit
9b9ef9c100
7 changed files with 334 additions and 215 deletions
8
.github/instructions/ai.instructions.md
vendored
8
.github/instructions/ai.instructions.md
vendored
|
|
@ -294,7 +294,7 @@ the Wizard's `Custom` path.
|
||||||
per-command Form permissions (`selectPermission`, `insertPermission`, `updatePermission`,
|
per-command Form permissions (`selectPermission`, `insertPermission`, `updatePermission`,
|
||||||
`deletePermission`). In `Otomatik` mode the base is the read permission the Wizard created for
|
`deletePermission`). In `Otomatik` mode the base is the read permission the Wizard created for
|
||||||
the component and the commands get `''` / `.Create` / `.Update` / `.Delete` suffixes.
|
the component and the commands get `''` / `.Create` / `.Update` / `.Delete` suffixes.
|
||||||
- List screen permissions: `App.DeveloperKit.CustomComponents{,.Create,.Update,.Delete}`.
|
- List screen permissions: `App.DeveloperKit.CustomComponents{,.Create,.Update,.Delete,.Export,.Import}`.
|
||||||
- Every save/delete is mirrored into `configs/seeds/{scope}/data/App.DeveloperKit.CustomComponents.json`; the endpoints it uses are
|
- Every save/delete is mirrored into `configs/seeds/{scope}/data/App.DeveloperKit.CustomComponents.json`; the endpoints it uses are
|
||||||
referenced through `crud/{Entity}.json`. A component that is not seed-backed is not done.
|
referenced through `crud/{Entity}.json`. A component that is not seed-backed is not done.
|
||||||
|
|
||||||
|
|
@ -657,10 +657,12 @@ a deploy. AI must learn and teach implementation flow primarily from these asset
|
||||||
- LanguagesData.json
|
- LanguagesData.json
|
||||||
- WizardDataSeeder.cs / CrudDataSeeder.cs — the seeders that read
|
- WizardDataSeeder.cs / CrudDataSeeder.cs — the seeders that read
|
||||||
surface B below
|
surface B below
|
||||||
|
- ListFormSeedDataApplier (Domain/Data/) — applies `data/{ListFormCode}.json` after every
|
||||||
|
contributor has run
|
||||||
|
|
||||||
**B. Runtime seeds (written by the running application)** — `configs/seeds/`, resolved through
|
**B. Runtime seeds (written by the running application)** — `configs/seeds/`, resolved through
|
||||||
`SeedPathResolver`. The Wizard, the Custom Components screen / Visual Designer and the CRUD
|
`SeedPathResolver`. The Wizard, the Custom Components screen / Visual Designer, the CRUD
|
||||||
Endpoint Manager write here; the
|
Endpoint Manager and every ListForm screen with a `SeedFilePath` write here; the
|
||||||
seeders above restore them when the database is recreated. Scope folders mirror the CDN layout:
|
seeders above restore them when the database is recreated. Scope folders mirror the CDN layout:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
||||||
13
.github/instructions/lowcode.instructions.md
vendored
13
.github/instructions/lowcode.instructions.md
vendored
|
|
@ -1490,6 +1490,9 @@ Kurallar:
|
||||||
- `AuthName` boş bırakılırsa buton **herkese görünür**. Süreç butonlarında bu alanı boş bırakma:
|
- `AuthName` boş bırakılırsa buton **herkese görünür**. Süreç butonlarında bu alanı boş bırakma:
|
||||||
yetki kontrolünden geçmeyen buton hiç çizilmez, gizlemenin tek doğru yolu budur.
|
yetki kontrolünden geçmeyen buton hiç çizilmez, gizlemenin tek doğru yolu budur.
|
||||||
- `Text` ve `Hint` dil anahtarıdır; çeviri `::` öneki eklenerek çözülür. Düz metin yazma.
|
- `Text` ve `Hint` dil anahtarıdır; çeviri `::` öneki eklenerek çözülür. Düz metin yazma.
|
||||||
|
- `Icon` bir **DevExtreme ikon adıdır** (`check`, `trash`, `doc`, `preferences` …); tema CSS'i
|
||||||
|
`dx-icon-<ad>` sınıfıyla çizer. Komut diyalogundaki alan hazır ikon listesini önerir, listede
|
||||||
|
olmayan bir ad da yazılabilir. Form ekranının araç çubuğu da aynı ikon adlarını kullanır.
|
||||||
- Toolbar butonu **seçili satırlarla** çalışır; `SelectionJson` uygun modda (`single`/`multiple`)
|
- Toolbar butonu **seçili satırlarla** çalışır; `SelectionJson` uygun modda (`single`/`multiple`)
|
||||||
olmalıdır. İş akışı varsa wizard bunu zaten `single` yapar.
|
olmalıdır. İş akışı varsa wizard bunu zaten `single` yapar.
|
||||||
- Satır bazlı aksiyon istiyorsan `ButtonPosition: 0` kullan; buton komut sütununda görünür.
|
- Satır bazlı aksiyon istiyorsan `ButtonPosition: 0` kullan; buton komut sütununda görünür.
|
||||||
|
|
@ -2356,7 +2359,7 @@ Form düğümünün `id`'si) ve Save/Delete üzerinden geri yazar. Olaylar: `onL
|
||||||
- `Özel` mod: yetki adı elle yazılır.
|
- `Özel` mod: yetki adı elle yazılır.
|
||||||
- Boş bırakılan komut serbesttir.
|
- Boş bırakılan komut serbesttir.
|
||||||
|
|
||||||
Liste ekranı yetkileri: `App.DeveloperKit.CustomComponents{,.Create,.Update,.Delete}`.
|
Liste ekranı yetkileri: `App.DeveloperKit.CustomComponents{,.Create,.Update,.Delete,.Export,.Import}`.
|
||||||
|
|
||||||
### 7.6 Bileşeni menüye bağlama
|
### 7.6 Bileşeni menüye bağlama
|
||||||
|
|
||||||
|
|
@ -2892,9 +2895,11 @@ Endpoint **tanımlarını** düzenleme yetkileri ListForm ekranının
|
||||||
`App.DeveloperKit.CustomEndpoints{,.Create,.Update,.Delete}` yetkileridir — çağırma
|
`App.DeveloperKit.CustomEndpoints{,.Create,.Update,.Delete}` yetkileridir — çağırma
|
||||||
yetkilerinden (`.Get/.Post/.Put/.Remove` + `PermissionsJson`) ayrıdır.
|
yetkilerinden (`.Get/.Post/.Put/.Remove` + `PermissionsJson`) ayrıdır.
|
||||||
|
|
||||||
**Seed:** ekrandan girilen kayıt yalnızca veritabanında yaşar. Proje teslimi için kaydı
|
**Seed:** platform varsayılanları `configs/seeds/{kapsam}/data/App.DeveloperKit.CustomEndpoints.json`
|
||||||
`sql/execute/{Ad}.sql` upsert prosedürüyle geri getirilebilir yap (§9.14 uyarısı); platform
|
dosyasından gelir; ekranın `SeedSyncInsert/Update/Delete` bayrakları **kapalıdır**, yani ekrandan
|
||||||
varsayılanı ise çekirdeğin `Seeds/TenantData.json` → `CustomEndpoints` bloğundadır.
|
girilen kayıt dosyaya yansımaz ve yalnızca veritabanında yaşar. Proje teslimi için kaydı ya bu
|
||||||
|
dosyaya elle ekle ya da `sql/execute/{Ad}.sql` upsert prosedürüyle geri getirilebilir yap
|
||||||
|
(§9.14 uyarısı).
|
||||||
|
|
||||||
**Ne zaman CRUD, ne zaman Custom Endpoint?** Tek tabloya standart CRUD gerekiyorsa CRUD Endpoint
|
**Ne zaman CRUD, ne zaman Custom Endpoint?** Tek tabloya standart CRUD gerekiyorsa CRUD Endpoint
|
||||||
(otomatik üretilir, `crud/{Entity}.json` ile seed'lenir, filtre sözleşmesi hazırdır).
|
(otomatik üretilir, `crud/{Entity}.json` ile seed'lenir, filtre sözleşmesi hazırdır).
|
||||||
|
|
|
||||||
12
README.md
12
README.md
|
|
@ -632,8 +632,8 @@ Teknik kullanıcılar için `/admin/developerkit` altında toplanan araçlar:
|
||||||
| **Custom Endpoint** | `/admin/list/App.DeveloperKit.CustomEndpoints` | SQL veya servis tanımından REST endpoint üretimi; GET/POST/PUT/DELETE için ayrı yetki kapıları (`App.DeveloperKit.CustomEndpoints.*`) ve kullanıcı/rol/global erişim kuralları. |
|
| **Custom Endpoint** | `/admin/list/App.DeveloperKit.CustomEndpoints` | SQL veya servis tanımından REST endpoint üretimi; GET/POST/PUT/DELETE için ayrı yetki kapıları (`App.DeveloperKit.CustomEndpoints.*`) ve kullanıcı/rol/global erişim kuralları. |
|
||||||
| **CRUD Endpoint** | SQL Query Manager içinde | Tablo tanımından tam CRUD endpoint kümesi üretimi. Ayrı ekranı yoktur; bkz. aşağıdaki not. |
|
| **CRUD Endpoint** | SQL Query Manager içinde | Tablo tanımından tam CRUD endpoint kümesi üretimi. Ayrı ekranı yoktur; bkz. aşağıdaki not. |
|
||||||
| **Dynamic Service** | `/admin/list/App.DeveloperKit.DynamicServices` | Liste bir ListForm ekranıdır (KPI şeridi, Swagger butonu, kart/ızgara görünümü); kod yazma listeden açılan `DynamicServiceEditor` diyalogundadır: Monaco editörü, Roslyn ile derleme (`TestCompile`), yayınlama (`Publish`) ve çalışan uygulamaya controller olarak kaydetme. Yetkiler: Create/Edit/Delete/Manage/TestCompile/Publish/ViewCode. |
|
| **Dynamic Service** | `/admin/list/App.DeveloperKit.DynamicServices` | Liste bir ListForm ekranıdır (KPI şeridi, Swagger butonu, kart/ızgara görünümü); kod yazma listeden açılan `DynamicServiceEditor` diyalogundadır: Monaco editörü, Roslyn ile derleme (`TestCompile`), yayınlama (`Publish`) ve çalışan uygulamaya controller olarak kaydetme. Yetkiler: Create/Edit/Delete/Manage/TestCompile/Publish/ViewCode. |
|
||||||
| **Custom Component** | `/admin/list/App.DeveloperKit.CustomComponents` | React bileşenini veritabanında saklama; `@babel/standalone` ile tarayıcıda derleyip route'a bağlama. Liste bir ListForm ekranıdır (KPI şeridi, ızgara/kart görünümü, grid'in kendi ekleme/düzenleme formu); satırdaki **Design** butonu `/admin/developerkit/components/design/{id}` tasarımcısını, **Open URL** butonu bileşenin `RoutePath`'ini açar. Kayıtlar `configs/seeds/host/data/App.DeveloperKit.CustomComponents.json` dosyasına senkronlanır. Yetkiler `App.DeveloperKit.CustomComponents{,.Create,.Update,.Delete}`. |
|
| **Custom Component** | `/admin/list/App.DeveloperKit.CustomComponents` | React bileşenini veritabanında saklama; `@babel/standalone` ile tarayıcıda derleyip route'a bağlama. Liste bir ListForm ekranıdır (KPI şeridi, ızgara/kart görünümü, grid'in kendi ekleme/düzenleme formu); satırdaki **Design** butonu `/admin/developerkit/components/design/{id}` tasarımcısını, **Open URL** butonu bileşenin `RoutePath`'ini açar. Kayıtlar `configs/seeds/host/data/App.DeveloperKit.CustomComponents.json` dosyasına senkronlanır. Yetkiler `App.DeveloperKit.CustomComponents{,.Create,.Update,.Delete,.Export,.Import}`. |
|
||||||
| **Visual Designer** | Component editörü içinde | Sürükle-bırak kanvas ile bileşen üretimi ve kod üretimi (`visualDesigner/codeGenerator.ts`). |
|
| **Visual Designer** | `/admin/developerkit/components/design/:id` | Custom Component listesindeki **Design** butonundan açılır; sürükle-bırak kanvas ile bileşen üretimi ve kod üretimi (`visualDesigner/codeGenerator.ts`). |
|
||||||
| **ListForm** | `/admin/list/App.Listforms.Listform` | Var olan ekranların ham tanım listesi; buradan `/admin/listform/edit/{kod}` sekmeli editörüne geçilir. |
|
| **ListForm** | `/admin/list/App.Listforms.Listform` | Var olan ekranların ham tanım listesi; buradan `/admin/listform/edit/{kod}` sekmeli editörüne geçilir. |
|
||||||
| **Wizard Manager** | `/admin/listform/wizardManager` | Üretilmiş wizard seed dosyalarının listesi/kartı, düzenleme, silme, DB migrate, export/import. Yetki `App.Listforms.Wizard{,.Create,.Update,.Delete,.Export,.Import}`. |
|
| **Wizard Manager** | `/admin/listform/wizardManager` | Üretilmiş wizard seed dosyalarının listesi/kartı, düzenleme, silme, DB migrate, export/import. Yetki `App.Listforms.Wizard{,.Create,.Update,.Delete,.Export,.Import}`. |
|
||||||
|
|
||||||
|
|
@ -783,7 +783,7 @@ içindedir; oradaki kontroller yalnızca butonları gizler, asıl kontrol AppSer
|
||||||
| Grup | Alt yetkiler |
|
| Grup | Alt yetkiler |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `App.Listforms.Wizard` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import` |
|
| `App.Listforms.Wizard` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import` |
|
||||||
| `App.DeveloperKit.CustomComponents` | `.Create`, `.Update`, `.Delete` |
|
| `App.DeveloperKit.CustomComponents` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import` |
|
||||||
| `App.DeveloperKit.CustomEndpoints` | `.Get`, `.Post`, `.Put`, `.Remove` (dispatcher üzerinden çağrı kapısı; endpoint bazlı User/Role/Global kuralları bunun üstünde denetlenir) |
|
| `App.DeveloperKit.CustomEndpoints` | `.Get`, `.Post`, `.Put`, `.Remove` (dispatcher üzerinden çağrı kapısı; endpoint bazlı User/Role/Global kuralları bunun üstünde denetlenir) |
|
||||||
| `App.DeveloperKit.DynamicServices` | `.Create`, `.Edit`, `.Delete`, `.Manage`, `.TestCompile`, `.Publish`, `.ViewCode` |
|
| `App.DeveloperKit.DynamicServices` | `.Create`, `.Edit`, `.Delete`, `.Manage`, `.TestCompile`, `.Publish`, `.ViewCode` |
|
||||||
| `App.SqlQueryManager` | `.CrudEndpoints` |
|
| `App.SqlQueryManager` | `.CrudEndpoints` |
|
||||||
|
|
@ -1197,9 +1197,12 @@ derlenip yayınlanır. Yeni bir modülün nasıl kurulacağını öğrenmek içi
|
||||||
- `LanguagesData.json` — dil anahtarları (EN/TR)
|
- `LanguagesData.json` — dil anahtarları (EN/TR)
|
||||||
- `WizardDataSeeder.cs`, `CrudDataSeeder.cs` — aşağıdaki
|
- `WizardDataSeeder.cs`, `CrudDataSeeder.cs` — aşağıdaki
|
||||||
runtime seed dosyalarını okuyup uygulayan seeder'lar
|
runtime seed dosyalarını okuyup uygulayan seeder'lar
|
||||||
|
- `ListFormSeedDataApplier` (`Domain/Data/`) — bütün contributor'lar bittikten sonra
|
||||||
|
`data/{ListFormCode}.json` dosyalarını uygular
|
||||||
|
|
||||||
**2. Runtime'da üretilen seed'ler** — `configs/seeds/`. Wizard, Custom Components ekranı/Visual
|
**2. Runtime'da üretilen seed'ler** — `configs/seeds/`. Wizard, Custom Components ekranı/Visual
|
||||||
Designer ve CRUD Endpoint Manager çalışma zamanında burayı yazar; veritabanı silinip yeniden oluşturulduğunda aynı dosyalar
|
Designer, CRUD Endpoint Manager ve `SeedFilePath` tanımlı ListForm ekranları çalışma zamanında
|
||||||
|
burayı yazar; veritabanı silinip yeniden oluşturulduğunda aynı dosyalar
|
||||||
geri yüklenir. Kapsam klasörü CDN düzeniyle aynıdır ve `SeedPathResolver` üzerinden çözülür:
|
geri yüklenir. Kapsam klasörü CDN düzeniyle aynıdır ve `SeedPathResolver` üzerinden çözülür:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -1207,6 +1210,7 @@ configs/seeds/
|
||||||
├── host/ # host kapsamı
|
├── host/ # host kapsamı
|
||||||
│ ├── wizard/ # {Ad}.json → WizardDataSeeder
|
│ ├── wizard/ # {Ad}.json → WizardDataSeeder
|
||||||
│ ├── crud/ # {EntityName}.json → CrudDataSeeder
|
│ ├── crud/ # {EntityName}.json → CrudDataSeeder
|
||||||
|
│ ├── data/ # {ListFormCode}.json → ListFormSeedDataApplier
|
||||||
│ ├── sql/{object,execute}/ # .sql (SQL Server)
|
│ ├── sql/{object,execute}/ # .sql (SQL Server)
|
||||||
│ └── postgres/{object,execute}/
|
│ └── postgres/{object,execute}/
|
||||||
├── tenants/{tenantId}/… # aynı düzen, tenant kapsamı
|
├── tenants/{tenantId}/… # aynı düzen, tenant kapsamı
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,10 @@ import { SelectBoxOption } from '@/types/shared'
|
||||||
import { useStoreActions, useStoreState } from '@/store'
|
import { useStoreActions, useStoreState } from '@/store'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { Field, FieldProps, Form, Formik } from 'formik'
|
import { Field, FieldProps, Form, Formik } from 'formik'
|
||||||
|
import CreatableSelect from 'react-select/creatable'
|
||||||
import { Dispatch, SetStateAction, useEffect, useState } from 'react'
|
import { Dispatch, SetStateAction, useEffect, useState } from 'react'
|
||||||
import { number, object, string } from 'yup'
|
import { number, object, string } from 'yup'
|
||||||
import { commandButtonPositionTypeOptions, urlOptions } from '../options'
|
import { commandButtonIconOptions, commandButtonPositionTypeOptions, urlOptions } from '../options'
|
||||||
import { JsonRowDialogData } from './types'
|
import { JsonRowDialogData } from './types'
|
||||||
import {
|
import {
|
||||||
deleteListFormJsonRow,
|
deleteListFormJsonRow,
|
||||||
|
|
@ -248,13 +249,34 @@ function JsonRowOpDialogCommand({
|
||||||
invalid={errors.icon && touched.icon}
|
invalid={errors.icon && touched.icon}
|
||||||
errorMessage={errors.icon}
|
errorMessage={errors.icon}
|
||||||
>
|
>
|
||||||
<Field
|
<Field type="text" autoComplete="off" name="icon" placeholder="Icon">
|
||||||
type="text"
|
{({ field, form }: FieldProps<string>) => (
|
||||||
autoComplete="off"
|
<Select
|
||||||
name="icon"
|
field={field}
|
||||||
placeholder="Icon"
|
form={form}
|
||||||
component={Input}
|
isClearable={true}
|
||||||
/>
|
componentAs={CreatableSelect}
|
||||||
|
placeholder="Icon"
|
||||||
|
options={commandButtonIconOptions}
|
||||||
|
value={
|
||||||
|
values.icon
|
||||||
|
? (commandButtonIconOptions.find(
|
||||||
|
(option) => option.value === values.icon,
|
||||||
|
) ?? { value: values.icon, label: values.icon })
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
formatOptionLabel={(option: SelectBoxOption) => (
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
<i className={`dx-icon dx-icon-${option.value}`} />
|
||||||
|
{option.label}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
onChange={(option: SelectBoxOption | null) =>
|
||||||
|
form.setFieldValue(field.name, option?.value ?? '')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem
|
<FormItem
|
||||||
label="Is Visible"
|
label="Is Visible"
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import {
|
||||||
} from '../../../../proxy/admin/list-form-field/models'
|
} from '../../../../proxy/admin/list-form-field/models'
|
||||||
import { enumToList } from '../../../../utils/enumUtils'
|
import { enumToList } from '../../../../utils/enumUtils'
|
||||||
import { PREFIX } from '@/constants/theme.constant'
|
import { PREFIX } from '@/constants/theme.constant'
|
||||||
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
|
|
||||||
export enum WidgetColorEnum {
|
export enum WidgetColorEnum {
|
||||||
'Blue' = 'blue',
|
'Blue' = 'blue',
|
||||||
|
|
@ -696,3 +697,112 @@ export const styleMapOptions = enumToList<string>(StyleEnum).map((opt) => ({
|
||||||
...opt,
|
...opt,
|
||||||
color: styleColorMap[opt.value],
|
color: styleColorMap[opt.value],
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grid/Toolbar komut butonlarının ikon seçenekleri. Değerler DevExtreme ikon
|
||||||
|
* adlarıdır (`dx-icon-<ad>`); butonlar ikonu doğrudan bu adla çizer.
|
||||||
|
*/
|
||||||
|
export const commandButtonIconOptions: SelectBoxOption[] = [
|
||||||
|
'add',
|
||||||
|
'plus',
|
||||||
|
'minus',
|
||||||
|
'remove',
|
||||||
|
'trash',
|
||||||
|
'edit',
|
||||||
|
'save',
|
||||||
|
'undo',
|
||||||
|
'revert',
|
||||||
|
'refresh',
|
||||||
|
'clear',
|
||||||
|
'close',
|
||||||
|
'check',
|
||||||
|
'apply',
|
||||||
|
'checklist',
|
||||||
|
'copy',
|
||||||
|
'paste',
|
||||||
|
'cut',
|
||||||
|
'search',
|
||||||
|
'find',
|
||||||
|
'filter',
|
||||||
|
'sorted',
|
||||||
|
'columnchooser',
|
||||||
|
'fields',
|
||||||
|
'preferences',
|
||||||
|
'card',
|
||||||
|
'detailslayout',
|
||||||
|
'contentlayout',
|
||||||
|
'menu',
|
||||||
|
'more',
|
||||||
|
'overflow',
|
||||||
|
'export',
|
||||||
|
'exportpdf',
|
||||||
|
'exportxlsx',
|
||||||
|
'exportselected',
|
||||||
|
'import',
|
||||||
|
'upload',
|
||||||
|
'download',
|
||||||
|
'doc',
|
||||||
|
'docfile',
|
||||||
|
'docxfile',
|
||||||
|
'pdffile',
|
||||||
|
'xlsfile',
|
||||||
|
'xlsxfile',
|
||||||
|
'csv',
|
||||||
|
'file',
|
||||||
|
'folder',
|
||||||
|
'print',
|
||||||
|
'email',
|
||||||
|
'message',
|
||||||
|
'comment',
|
||||||
|
'bell',
|
||||||
|
'share',
|
||||||
|
'link',
|
||||||
|
'globe',
|
||||||
|
'home',
|
||||||
|
'user',
|
||||||
|
'group',
|
||||||
|
'key',
|
||||||
|
'lock',
|
||||||
|
'unlock',
|
||||||
|
'verticalaligntop',
|
||||||
|
'chart',
|
||||||
|
'datapie',
|
||||||
|
'datausage',
|
||||||
|
'money',
|
||||||
|
'cart',
|
||||||
|
'product',
|
||||||
|
'box',
|
||||||
|
'photo',
|
||||||
|
'image',
|
||||||
|
'video',
|
||||||
|
'attach',
|
||||||
|
'clock',
|
||||||
|
'event',
|
||||||
|
'todo',
|
||||||
|
'runner',
|
||||||
|
'repeat',
|
||||||
|
'info',
|
||||||
|
'help',
|
||||||
|
'warning',
|
||||||
|
'errorcircle',
|
||||||
|
'checkmarkcircle',
|
||||||
|
'favorites',
|
||||||
|
'like',
|
||||||
|
'pin',
|
||||||
|
'map',
|
||||||
|
'tel',
|
||||||
|
'back',
|
||||||
|
'chevronleft',
|
||||||
|
'chevronright',
|
||||||
|
'chevrondown',
|
||||||
|
'chevronup',
|
||||||
|
'arrowleft',
|
||||||
|
'arrowright',
|
||||||
|
'arrowup',
|
||||||
|
'arrowdown',
|
||||||
|
'expand',
|
||||||
|
'collapse',
|
||||||
|
'fullscreen',
|
||||||
|
'hierarchy',
|
||||||
|
'floppy',
|
||||||
|
].map((icon) => ({ value: icon, label: icon }))
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,13 @@
|
||||||
import { Badge, Button, Dialog, Notification, toast } from '@/components/ui'
|
import { Button, Dialog, Notification, toast } from '@/components/ui'
|
||||||
import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
|
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import CustomStore from 'devextreme/data/custom_store'
|
import CustomStore from 'devextreme/data/custom_store'
|
||||||
|
import Toolbar from 'devextreme-react/toolbar'
|
||||||
|
import type { dxToolbarItem as DxToolbarItem } from 'devextreme/ui/toolbar'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import {
|
|
||||||
FaChevronLeft,
|
|
||||||
FaTrashAlt,
|
|
||||||
FaPlusCircle,
|
|
||||||
FaPencilAlt,
|
|
||||||
FaEye,
|
|
||||||
FaSave,
|
|
||||||
FaCog,
|
|
||||||
FaSistrix,
|
|
||||||
} from 'react-icons/fa'
|
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { GridColumnData } from '../list/GridColumnData'
|
import { GridColumnData } from '../list/GridColumnData'
|
||||||
import { isWorkflowToolbarItemDisabled, useToolbar } from '../list/useToolbar'
|
import { isWorkflowToolbarItemDisabled, useToolbar } from '../list/useToolbar'
|
||||||
|
import { orderListToolbarItems } from '../list/toolbarOrder'
|
||||||
import { PermissionResults, RowMode } from './types'
|
import { PermissionResults, RowMode } from './types'
|
||||||
import { GridDto } from '@/proxy/form/models'
|
import { GridDto } from '@/proxy/form/models'
|
||||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||||
|
|
@ -162,194 +154,178 @@ const FormButtons = (props: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const navigationIcon = useNavigationIcons()
|
|
||||||
|
|
||||||
const toolbarExcludedNames = ['deleteAllRecords', 'deleteSelectedRecords', 'refreshButton']
|
const toolbarExcludedNames = ['deleteAllRecords', 'deleteSelectedRecords', 'refreshButton']
|
||||||
const hasVisibleToolbarButtons = toolbarData?.some(isToolbarButtonVisible)
|
|
||||||
|
|
||||||
const commandExcludedNames = ['edit', 'delete', 'detail']
|
const commandExcludedNames = ['edit', 'delete', 'detail']
|
||||||
const hasVisibleCommandButtons = commandColumnData?.buttons?.some(isCommandButtonVisible)
|
|
||||||
|
/** Grid toolbar sozlesmesiyle ayni bicimde bir dxButton ogesi uretir. */
|
||||||
|
const buildButtonItem = (
|
||||||
|
icon: string,
|
||||||
|
text: string,
|
||||||
|
onClick: (e: any) => void,
|
||||||
|
disabled = false,
|
||||||
|
): DxToolbarItem => ({
|
||||||
|
location: 'after',
|
||||||
|
widget: 'dxButton',
|
||||||
|
locateInMenu: 'auto',
|
||||||
|
showText: 'always',
|
||||||
|
options: { icon, text, hint: text, disabled, onClick },
|
||||||
|
})
|
||||||
|
|
||||||
|
const toolbarItems: DxToolbarItem[] = [
|
||||||
|
// Liste toolbar'indan gelen ozel butonlar; grid ile ayni sirayi korur.
|
||||||
|
...orderListToolbarItems(toolbarData?.filter(isToolbarButtonVisible) ?? []).map(
|
||||||
|
(item): DxToolbarItem => ({
|
||||||
|
location: 'after',
|
||||||
|
widget: 'dxButton',
|
||||||
|
locateInMenu: 'auto',
|
||||||
|
showText: 'always',
|
||||||
|
options: {
|
||||||
|
...item.options,
|
||||||
|
// Gorunurluk zaten filtrelendi; DX'e fonksiyon gecilmemeli.
|
||||||
|
visible: true,
|
||||||
|
disabled: isToolbarButtonDisabled(item),
|
||||||
|
onClick: (e: any) => item.options?.onClick?.({ ...e, ...rowEvent }),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Komut kolonundan toolbar'a tasinan butonlar.
|
||||||
|
...(commandColumnData?.buttons?.filter(isCommandButtonVisible) ?? []).map(
|
||||||
|
(item: any): DxToolbarItem => ({
|
||||||
|
location: 'after',
|
||||||
|
widget: 'dxButton',
|
||||||
|
locateInMenu: 'auto',
|
||||||
|
showText: 'always',
|
||||||
|
options: {
|
||||||
|
icon: item.icon,
|
||||||
|
text: item.text,
|
||||||
|
hint: item.hint ?? item.text,
|
||||||
|
onClick: (e: any) => item.onClick?.({ ...e, ...rowEvent }),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
if (!isSubForm) {
|
||||||
|
toolbarItems.push(
|
||||||
|
buildButtonItem('back', translate('::App.Platform.Cancel'), () => {
|
||||||
|
if (onActionView && id) {
|
||||||
|
onActionView()
|
||||||
|
} else {
|
||||||
|
navigate(-1)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode !== 'new') {
|
||||||
|
toolbarItems.push(
|
||||||
|
buildButtonItem(
|
||||||
|
'trash',
|
||||||
|
translate('::App.Platform.Delete'),
|
||||||
|
() => setDeleteRowId(id),
|
||||||
|
!permissions.d,
|
||||||
|
),
|
||||||
|
buildButtonItem(
|
||||||
|
'add',
|
||||||
|
translate('::App.Platform.AddNew'),
|
||||||
|
() => {
|
||||||
|
if (onActionNew) {
|
||||||
|
onActionNew()
|
||||||
|
} else {
|
||||||
|
navigate(
|
||||||
|
ROUTES_ENUM.protected.admin.formNew.replace(
|
||||||
|
':listFormCode',
|
||||||
|
encodeURIComponent(listFormCode),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
!permissions.c,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'view') {
|
||||||
|
toolbarItems.push(
|
||||||
|
buildButtonItem(
|
||||||
|
'edit',
|
||||||
|
translate('::App.Platform.Edit'),
|
||||||
|
() => {
|
||||||
|
if (onActionEdit) {
|
||||||
|
onActionEdit()
|
||||||
|
} else {
|
||||||
|
navigate(
|
||||||
|
ROUTES_ENUM.protected.admin.formEdit
|
||||||
|
.replace(':listFormCode', encodeURIComponent(listFormCode))
|
||||||
|
.replace(':id', encodeURIComponent(id!)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
!permissions.u,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'edit' || (onActionView && mode === 'new')) {
|
||||||
|
toolbarItems.push(
|
||||||
|
buildButtonItem(
|
||||||
|
'eyeopen',
|
||||||
|
translate('::App.Platform.Detail'),
|
||||||
|
() => {
|
||||||
|
if (onActionView) {
|
||||||
|
onActionView()
|
||||||
|
} else {
|
||||||
|
navigate(
|
||||||
|
ROUTES_ENUM.protected.admin.formView
|
||||||
|
.replace(':listFormCode', encodeURIComponent(listFormCode))
|
||||||
|
.replace(':id', encodeURIComponent(id!)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
!permissions.r,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'edit' || mode === 'new') {
|
||||||
|
toolbarItems.push(
|
||||||
|
buildButtonItem(
|
||||||
|
'save',
|
||||||
|
translate('::App.Platform.Save'),
|
||||||
|
handleSubmit,
|
||||||
|
!(permissions.c || permissions.u),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id && !isSubForm) {
|
||||||
|
toolbarItems.push(
|
||||||
|
buildButtonItem(
|
||||||
|
'doc',
|
||||||
|
translate('::App.Reports.Form'),
|
||||||
|
openDynamicGridReport,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkPermission('App.Listforms.Listform.Update') && !isSubForm) {
|
||||||
|
toolbarItems.push(
|
||||||
|
buildButtonItem('preferences', translate('::App.Platform.Manage'), () => {
|
||||||
|
window.open(
|
||||||
|
ROUTES_ENUM.protected.saas.listFormManagement.edit.replace(
|
||||||
|
':listFormCode',
|
||||||
|
listFormCode,
|
||||||
|
),
|
||||||
|
isPwaMode ? '_self' : '_blank',
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-row items-center gap-1">
|
<Toolbar className="list-view-toolbar-host flex-1" items={toolbarItems} />
|
||||||
{toolbarData
|
|
||||||
?.filter(isToolbarButtonVisible)
|
|
||||||
.map((item, i) => {
|
|
||||||
const IconComp = navigationIcon[item.options?.icon]
|
|
||||||
const hasValidIcon =
|
|
||||||
IconComp &&
|
|
||||||
(typeof IconComp === 'function' ||
|
|
||||||
(typeof IconComp === 'object' && 'render' in IconComp))
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
key={'toolbarButton-' + i}
|
|
||||||
variant="default"
|
|
||||||
size="sm"
|
|
||||||
icon={hasValidIcon ? <IconComp className="text-gray-400" /> : null}
|
|
||||||
disabled={isToolbarButtonDisabled(item)}
|
|
||||||
onClick={(e) => item.options?.onClick?.({ ...e, ...rowEvent })}
|
|
||||||
>
|
|
||||||
{item.options?.text}
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
|
|
||||||
{hasVisibleToolbarButtons && <Badge innerClass="bg-blue-500" />}
|
|
||||||
|
|
||||||
{commandColumnData?.buttons
|
|
||||||
?.filter(isCommandButtonVisible)
|
|
||||||
.map((item: any, i) => {
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
key={'commandColumnButton-' + i}
|
|
||||||
variant="default"
|
|
||||||
size="sm"
|
|
||||||
title={item.hint}
|
|
||||||
onClick={(e: any) => {
|
|
||||||
if (item.onClick) {
|
|
||||||
item.onClick({ ...e, ...rowEvent })
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{item.text}
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
|
|
||||||
{hasVisibleCommandButtons && <Badge innerClass="bg-blue-500" />}
|
|
||||||
|
|
||||||
{!isSubForm && (
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="default"
|
|
||||||
icon={<FaChevronLeft />}
|
|
||||||
title={translate('::App.Platform.Cancel')}
|
|
||||||
onClick={() => {
|
|
||||||
if (onActionView && id) {
|
|
||||||
onActionView()
|
|
||||||
} else {
|
|
||||||
navigate(-1)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
></Button>
|
|
||||||
)}
|
|
||||||
{mode != 'new' && (
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="solid"
|
|
||||||
icon={<FaTrashAlt />}
|
|
||||||
title={translate('::App.Platform.Delete')}
|
|
||||||
onClick={() => {
|
|
||||||
setDeleteRowId(id)
|
|
||||||
}}
|
|
||||||
{...(permissions.d ? {} : { disabled: true })}
|
|
||||||
></Button>
|
|
||||||
)}
|
|
||||||
{mode != 'new' && (
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="solid"
|
|
||||||
color='blue-500'
|
|
||||||
icon={<FaPlusCircle />}
|
|
||||||
title={translate('::App.Platform.AddNew')}
|
|
||||||
onClick={() => {
|
|
||||||
if (onActionNew) {
|
|
||||||
onActionNew()
|
|
||||||
} else {
|
|
||||||
navigate(
|
|
||||||
ROUTES_ENUM.protected.admin.formNew.replace(
|
|
||||||
':listFormCode',
|
|
||||||
encodeURIComponent(listFormCode),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
{...(permissions.c ? {} : { disabled: true })}
|
|
||||||
></Button>
|
|
||||||
)}
|
|
||||||
{mode == 'view' && (
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="solid"
|
|
||||||
color="green-600"
|
|
||||||
icon={<FaPencilAlt />}
|
|
||||||
title={translate('::App.Platform.Edit')}
|
|
||||||
onClick={() => {
|
|
||||||
if (onActionEdit) {
|
|
||||||
onActionEdit()
|
|
||||||
} else {
|
|
||||||
navigate(
|
|
||||||
ROUTES_ENUM.protected.admin.formEdit
|
|
||||||
.replace(':listFormCode', encodeURIComponent(listFormCode))
|
|
||||||
.replace(':id', encodeURIComponent(id!)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
{...(permissions.u ? {} : { disabled: true })}
|
|
||||||
></Button>
|
|
||||||
)}
|
|
||||||
{(mode == 'edit' || (onActionView && mode == 'new')) && (
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="solid"
|
|
||||||
color="gray-500"
|
|
||||||
icon={<FaEye />}
|
|
||||||
title={translate('::App.Platform.Detail')}
|
|
||||||
onClick={() => {
|
|
||||||
if (onActionView) {
|
|
||||||
onActionView()
|
|
||||||
} else {
|
|
||||||
navigate(
|
|
||||||
ROUTES_ENUM.protected.admin.formView
|
|
||||||
.replace(':listFormCode', encodeURIComponent(listFormCode))
|
|
||||||
.replace(':id', encodeURIComponent(id!)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
{...(permissions.r ? {} : { disabled: true })}
|
|
||||||
></Button>
|
|
||||||
)}
|
|
||||||
{(mode == 'edit' || mode == 'new') && (
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="solid"
|
|
||||||
icon={<FaSave />}
|
|
||||||
title={translate('::App.Platform.Save')}
|
|
||||||
onClick={handleSubmit}
|
|
||||||
{...(permissions.c || permissions.u ? {} : { disabled: true })}
|
|
||||||
></Button>
|
|
||||||
)}
|
|
||||||
{id && !isSubForm && (
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="solid"
|
|
||||||
color="purple-500"
|
|
||||||
icon={<FaSistrix />}
|
|
||||||
title={translate('::App.Reports.Form')}
|
|
||||||
onClick={openDynamicGridReport}
|
|
||||||
></Button>
|
|
||||||
)}
|
|
||||||
{checkPermission('App.Listforms.Listform.Update') && !isSubForm && (
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="solid"
|
|
||||||
icon={<FaCog />}
|
|
||||||
color="green-500"
|
|
||||||
title={translate('::App.Platform.Manage')}
|
|
||||||
onClick={() => {
|
|
||||||
window.open(
|
|
||||||
ROUTES_ENUM.protected.saas.listFormManagement.edit.replace(
|
|
||||||
':listFormCode',
|
|
||||||
listFormCode,
|
|
||||||
),
|
|
||||||
isPwaMode ? '_self' : '_blank',
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
></Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<Dialog
|
<Dialog
|
||||||
isOpen={toolbarModalData?.open || false}
|
isOpen={toolbarModalData?.open || false}
|
||||||
onClose={() => setToolbarModalData(undefined)}
|
onClose={() => setToolbarModalData(undefined)}
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ const Blog: React.FC = () => {
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
placeholder={translateLabel(translate, 'App.PublicBlog.SearchPlaceholder')}
|
placeholder={translateLabel(translate, 'App.PublicBlog.SearchPlaceholder')}
|
||||||
aria-label={translate('::App.Platform.Search3')}
|
aria-label={translate('::App.Platform.Search3')}
|
||||||
className="w-full rounded-lg border border-gray-300 bg-white py-2 pl-10 text-gray-900 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400"
|
className="rounded-lg border border-gray-300 bg-white py-2 pl-10 text-gray-900 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400"
|
||||||
onChange={(event) => setSearchQuery(event.target.value)}
|
onChange={(event) => setSearchQuery(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<FaSearch className="absolute left-3 top-2.5 h-5 w-5 text-gray-400" />
|
<FaSearch className="absolute left-3 top-2.5 h-5 w-5 text-gray-400" />
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue