editorScript ve Seeder

This commit is contained in:
Sedat ÖZTÜRK 2025-10-22 17:58:27 +03:00
parent 65af5fa231
commit ea37ef0682
15 changed files with 324 additions and 296 deletions

View file

@ -46,5 +46,6 @@ public class EditingFormItemDto
public bool IsRequired { get; set; }
public GridBoxOptionsDto GridBoxOptions { get; set; }
public TagBoxOptionsDto TagBoxOptions { get; set; }
public string Script { get; set; }
[JsonPropertyName("editorScript")]
public string EditorScript { get; set; }
}

View file

@ -35399,9 +35399,9 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
new EditingFormItemDto { Order = 1, DataField = "AppliedDate", ColSpan = 2, IsRequired=true, EditorType2 = EditorTypes.dxDateBox},
new EditingFormItemDto { Order = 2, DataField = "EmployeeId", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox },
new EditingFormItemDto { Order = 3, DataField = "LeaveType", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox },
new EditingFormItemDto { Order = 4, DataField = "StartDate", ColSpan = 2, IsRequired=true, EditorType2 = EditorTypes.dxDateBox},
new EditingFormItemDto { Order = 5, DataField = "EndDate", ColSpan = 2, IsRequired=true, EditorType2 = EditorTypes.dxDateBox},
new EditingFormItemDto { Order = 6, DataField = "TotalDays", ColSpan = 2, EditorType2 = EditorTypes.dxNumberBox},
new EditingFormItemDto { Order = 4, DataField = "StartDate", ColSpan = 2, IsRequired=true, EditorType2 = EditorTypes.dxDateBox, EditorScript = EditorOptionValues.DateDiffStartDateEndDate},
new EditingFormItemDto { Order = 5, DataField = "EndDate", ColSpan = 2, IsRequired=true, EditorType2 = EditorTypes.dxDateBox, EditorScript = EditorOptionValues.DateDiffStartDateEndDate},
new EditingFormItemDto { Order = 6, DataField = "TotalDays", ColSpan = 2, EditorType2 = EditorTypes.dxNumberBox, EditorOptions = EditorOptionValues.Disabled},
new EditingFormItemDto { Order = 7, DataField = "IsHalfDay", ColSpan = 2, EditorType2 = EditorTypes.dxCheckBox},
new EditingFormItemDto { Order = 8, DataField = "Reason", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextArea},
new EditingFormItemDto { Order = 9, DataField = "Status", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox},

View file

@ -19,9 +19,11 @@ public static class PlatformConsts
public static class EditorOptionValues
{
public static string Disabled = "{ \"disabled\" : true }";
public static string ShowClearButton = "{ \"showClearButton\" : true }";
public static string HtmlEditorOptions = "{\"toolbar\": {\"multiline\": true, \"items\": [{\"name\": \"undo\"},{\"name\": \"redo\"},{\"name\": \"separator\"},{\"name\": \"size\",\"acceptedValues\": [\"8pt\",\"10pt\",\"12pt\",\"14pt\",\"18pt\",\"24pt\",\"36pt\"],\"options\": {\"inputAttr\": {\"aria-label\": \"Font size\"}}},{\"name\": \"font\",\"acceptedValues\": [\"Arial\",\"Courier New\",\"Georgia\",\"Impact\",\"Lucida Console\",\"Tahoma\",\"Times New Roman\",\"Verdana\"],\"options\": {\"inputAttr\": {\"aria-label\": \"Font family\"}}},{\"name\": \"separator\"},{\"name\": \"bold\"},{\"name\": \"italic\"},{\"name\": \"strike\"},{\"name\": \"underline\"},{\"name\": \"separator\"},{\"name\": \"alignLeft\"},{\"name\": \"alignCenter\"},{\"name\": \"alignRight\"},{\"name\": \"alignJustify\"},{\"name\": \"separator\"},{\"name\": \"orderedList\"},{\"name\": \"bulletList\"},{\"name\": \"separator\"},{\"name\": \"header\",\"acceptedValues\": [false,1,2,3,4,5],\"options\": {\"inputAttr\": {\"aria-label\": \"Font family\"}}},{\"name\": \"separator\"},{\"name\": \"color\"},{\"name\": \"background\"},{\"name\": \"separator\"},{\"name\": \"link\"},{\"name\": \"image\"},{\"name\": \"separator\"},{\"name\": \"clear\"},{\"name\": \"codeBlock\"},{\"name\": \"blockquote\"},{\"name\": \"separator\"},{\"name\": \"insertTable\"},{\"name\": \"deleteTable\"},{\"name\": \"insertRowAbove\"},{\"name\": \"insertRowBelow\"},{\"name\": \"deleteRow\"},{\"name\": \"insertColumnLeft\"},{\"name\": \"insertColumnRight\"},{\"name\": \"deleteColumn\"}]}}";
public static string PhoneEditorOptions = "{\"format\": \"phoneGlobal\", \"mask\":\"(000) 000-0000\", \"maskInvalidMessage\":\"Lütfen geçerli bir telefon numarası girin\", \"useMaskedValue\":false, \"maskRules\": { \"X\": \"[1-9]\" }, \"placeholder\": \"(555) 123-4567\" }";
public static string DateDiffStartDateEndDate = "(() => {const d=v=>!v?null:(v instanceof Date?v:new Date(v));const nf={...formData,[editor.dataField]:e?.value};const s=d(nf.StartDate),t=d(nf.EndDate);setFormData({...formData,TotalDays: s&&t?Math.max(0,Math.floor((Date.UTC(t.getFullYear(),t.getMonth(),t.getDate())-Date.UTC(s.getFullYear(),s.getMonth(),s.getDate()))/(24*60*60*1000))+1):null});})();";
}
public static class Prefix

View file

@ -2719,14 +2719,14 @@
"district": "Ankara",
"street": "Kızılay Cd. No:12",
"postalCode": "06050",
"phone": "+90 212 555 0100",
"personalPhone": "+90 532 555 0101",
"phone": "2125550100",
"personalPhone": "5325550101",
"email": "ali.ozturk@company.com",
"address1": "",
"address2": "",
"emergencyContactname": "Ayşe Öztürk",
"emergencyContactrelationship": "Eşi",
"emergencyContactphone": "+90 532 555 0100",
"emergencyContactphone": "5325550100",
"hireDate": "15-01-2020",
"terminationDate": "15-01-2020",
"employmentTypeName": "FullTime",
@ -2759,14 +2759,14 @@
"district": "Ankara",
"street": "İnönü Bulvarı No:456",
"postalCode": "06000",
"phone": "+90 212 555 0102",
"personalPhone": "+90 532 555 0103",
"phone": "2125550102",
"personalPhone": "5325550103",
"email": "ayse.kaya@company.com",
"address1": "",
"address2": "",
"emergencyContactname": "Fatma Kaya",
"emergencyContactrelationship": "Anne",
"emergencyContactphone": "+90 532 555 0104",
"emergencyContactphone": "5325550104",
"hireDate": "01-06-2021",
"terminationDate": "",
"employmentTypeName": "FullTime",

View file

@ -416,7 +416,7 @@ export interface EditingFormItemDto {
isRequired?: boolean
gridBoxOptions?: GridBoxOptionsDto
tagBoxOptions?: TagBoxOptionsDto
script?: string
editorScript?: string
}
export interface GridEditingPopupDto {

View file

@ -17,6 +17,7 @@ import {
UiCommandButtonPositionTypeEnum,
UiLookupDataSourceTypeEnum,
} from '../proxy/form/models'
import { useEffect } from 'react'
const cellTemplateMultiValue = (
cellElement: HTMLElement,
@ -82,6 +83,22 @@ function calculateFilterExpressionMultiValue(
}
}
// lookup cache (module scope)
const __lookupCache = new Map<string, Promise<any[]>>()
const cachedLoader = (key: string, loader: () => Promise<any[]>) => {
if (__lookupCache.has(key)) return __lookupCache.get(key)!
const p = Promise.resolve()
.then(() => loader())
.then((res) => res ?? [])
.catch((err) => {
__lookupCache.delete(key) // hata olursa tekrar denenebilsin
throw err
})
__lookupCache.set(key, p)
return p
}
const useListFormColumns = ({
gridDto,
listFormCode,
@ -98,6 +115,11 @@ const useListFormColumns = ({
const { checkPermission } = usePermission()
const isPwaMode = usePWA()
useEffect(() => {
// listFormCode değişince lookup cache temizlensin (farklı form farklı lookuplar)
__lookupCache.clear()
}, [listFormCode])
const lookupDataSource = (options: any, colData: any, listFormCode: string) => {
const { lookupDto } = colData
const filters = []
@ -124,6 +146,7 @@ const useListFormColumns = ({
return createLookupStaticDataSource(
() => JSON.parse(lookupDto?.lookupQuery),
filters.length ? filters : null,
`static:${listFormCode}:${colData.fieldName}`, // cache key
)
} else if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.Query) {
return createLookupQueryDataSource(listFormCode, colData.fieldName, filters)
@ -144,13 +167,16 @@ const useListFormColumns = ({
const createLookupStaticDataSource = (
load: () => any,
filter: any = null,
key: any = 'key',
key: any = 'static',
sort: any = 'name',
) => ({
store: new CustomStore({
key,
loadMode: 'raw',
load,
load: async (loadOptions) => {
// load fonksiyonu sync sonuç döndürüyor olabilir, o yüzden Promise.resolve ile sar
return cachedLoader(`static:${key}`, () => Promise.resolve(load()))
},
}),
sort,
filter,
@ -169,27 +195,21 @@ const useListFormColumns = ({
}
try {
const cacheKey = `query:${listFormCode}:${listFormFieldName}:${JSON.stringify(filters ?? null)}`
return cachedLoader(cacheKey, async () => {
const response = await dynamicFetch('list-form-select/lookup', 'POST', null, {
listFormCode,
listFormFieldName,
filters,
})
return response.data.map((a: any) => ({
return (response.data ?? []).map((a: any) => ({
key: a.Key,
name: a.Name,
group: a.Group,
}))
})
} catch (error: any) {
// toast.push(
// <Notification type="danger" duration={2000}>
// Select error
// {error.toString()}
// </Notification>,
// {
// placement: 'top-end',
// },
// )
return null
}
},
@ -206,7 +226,6 @@ const useListFormColumns = ({
key: keyName,
loadMode: 'raw',
load: async (loadOptions) => {
//Sayfa degismisse istek yapma
if (!isSubForm && listFormCode && !window.location.pathname.includes(listFormCode)) {
return
}
@ -215,48 +234,29 @@ const useListFormColumns = ({
}
const [method, url, body, keySelector, nameSelector, groupSelector] = lookupQuery.split(';')
//POST;https://tcmb.gov.tr/api/doviz;{"dovizType":@param0};a=>a.DovizId;a=>a.DovizName;a=>a.Ulke
//TODO: Body dinamik params test edilecek
// body içindeki @paramN'leri filtrelerle değiştir
let resolvedBody = body
if (filters?.length) {
for (let i = 0; i < filters.length; i++) {
body.replace(`@param${i}`, filters[i])
resolvedBody = resolvedBody.replace(new RegExp(`@param${i}`, 'g'), filters[i])
}
}
try {
const response = await dynamicFetch(url, method, null, body)
const cacheKey = `api:${lookupQuery}:${JSON.stringify(filters ?? null)}`
return cachedLoader(cacheKey, async () => {
const response = await dynamicFetch(url, method, null, resolvedBody)
let { data } = response
if (!data) {
// toast.push(
// <Notification type="danger" duration={2000}>
// {'Lookup Datası boş geldi.'}
// </Notification>,
// {
// placement: 'top-end',
// },
// )
return
}
if (!Array.isArray(data)) {
data = [data]
}
if (!data) return []
if (!Array.isArray(data)) data = [data]
return data.map((a: any) => ({
key: eval(keySelector),
name: eval(nameSelector),
group: eval(groupSelector),
}))
})
} catch {
// toast.push(
// <Notification type="danger" duration={2000}>
// {'Network error'}
// </Notification>,
// {
// placement: 'top-end',
// },
// )
return
}
},

View file

@ -740,9 +740,9 @@ function JsonRowOpDialogEditForm({
<Field
type="text"
autoComplete="off"
name={`items.${index}.script`}
name={`items.${index}.editorScript`}
component={Input}
placeholder="Script"
placeholder="Editor Script"
/>
</div>
<div className="w-1/12 ml-2 align-middle text-center">

View file

@ -17,7 +17,6 @@ import { GridColumnData } from '../list/GridColumnData'
import { useToolbar } from '../list/useToolbar'
import { PermissionResults, RowMode } from './types'
import { GridDto } from '@/proxy/form/models'
import React from 'react'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { usePermission } from '@/utils/hooks/usePermission'
import { GridExtraFilterState } from '../list/Utils'
@ -214,7 +213,8 @@ const FormButtons = (props: {
if (onActionNew) {
onActionNew()
} else {
navigate(ROUTES_ENUM.protected.admin.formNew.replace(':listFormCode', listFormCode))
navigate(ROUTES_ENUM.protected.admin.formNew
.replace(':listFormCode', listFormCode))
}
}}
{...(permissions.c ? {} : { disabled: true })}

View file

@ -41,12 +41,12 @@ const FormDevExpress = (props: {
.flatMap((group) => (group.items as SimpleItemWithColData[]) || [])
.find((i: SimpleItemWithColData) => i.dataField === e.dataField)
if (changeItem?.script) {
if (changeItem?.editorScript) {
try {
//setFormData({...formData, Path: e.value});
//UiEvalService.ApiGenerateBackgroundWorkers();
//setFormData({ ...formData, Path: (v => v === '1' ? '1-deneme' : v === '0' ? '0-deneme' : '')(e.value) })
eval(changeItem.script)
eval(changeItem.editorScript)
} catch (err) {
console.error('Script execution failed for', changeItem.name, err)
}

View file

@ -20,7 +20,7 @@ export type SimpleItemWithColData = Overwrite<
canUpdate: boolean
canExport: boolean
editorType2: EditorType2
script?: string
editorScript?: string
}
>
export type RowMode = 'view' | 'edit' | 'new'

View file

@ -261,7 +261,7 @@ const useGridData = (props: {
colData,
tagBoxOptions: i.tagBoxOptions,
gridBoxOptions: i.gridBoxOptions,
script: i.script,
editorScript: i.editorScript,
}
if (i.dataField.indexOf(':') >= 0) {
item.label = { text: captionize(i.dataField.split(':')[1]) }

View file

@ -1,18 +1,44 @@
// src/.../useLookupDataSource.ts
import { dynamicFetch } from '@/services/form.service'
import { UiLookupDataSourceTypeEnum } from '@/proxy/form/models'
import CustomStore from 'devextreme/data/custom_store'
import { useCallback } from 'react'
/** Module-scope cache: key -> Promise<array>
* - Aynı anda ılan editörler tek çağrısı üzerinden paylaşır
* - Hata olursa cache temizlenir (yeniden denenebilir)
*/
const __lookupCache = new Map<string, Promise<any[]>>()
const cachedLoader = (key: string, loader: () => Promise<any[]>) => {
if (__lookupCache.has(key)) return __lookupCache.get(key)!
const p = Promise.resolve()
.then(() => loader())
.then((res) => res ?? [])
.catch((err) => {
__lookupCache.delete(key)
throw err
})
__lookupCache.set(key, p)
return p
}
const createLookupStaticDataSource = (
load: () => any,
filter: any = null,
key: any = 'key',
key: any = 'static',
sort: any = 'name',
) => ({
store: new CustomStore({
key,
loadMode: 'raw',
load,
load: async () => {
const cacheKey = `static:${key}`
return cachedLoader(cacheKey, async () => {
const res = await Promise.resolve(load())
return Array.isArray(res) ? res : [res]
})
},
}),
sort,
filter,
@ -30,21 +56,21 @@ const createLookupQueryDataSource = (
if (!isSubForm && listFormCode && !window.location.pathname.includes(listFormCode)) {
return
}
try {
const cacheKey = `query:${listFormCode}:${listFormFieldName}:${JSON.stringify(filters ?? null)}`
return cachedLoader(cacheKey, async () => {
const response = await dynamicFetch('list-form-select/lookup', 'POST', null, {
listFormCode,
listFormFieldName,
filters,
})
return response.data.map((a: any) => ({
const data = response?.data ?? []
return (Array.isArray(data) ? data : [data]).map((a: any) => ({
key: a.Key,
name: a.Name,
group: a.Group,
}))
} catch (error: any) {
return null
}
}).catch(() => null as any)
},
})
}
@ -67,33 +93,35 @@ const createLookupApiDataSource = (
return
}
const [method, url, body, keySelector, nameSelector, groupSelector] = lookupQuery.split(';')
const parts = lookupQuery.split(';')
const [
method = 'GET',
url = '',
bodyTemplate = '',
keySelector = 'a=>a.id',
nameSelector = 'a=>a.name',
groupSelector = 'a=>a.group',
] = parts
let resolvedBody = bodyTemplate ?? ''
if (filters?.length) {
for (let i = 0; i < filters.length; i++) {
body.replace(`@param${i}`, filters[i])
resolvedBody = resolvedBody.replace(new RegExp(`@param${i}`, 'g'), String(filters[i]))
}
}
try {
const response = await dynamicFetch(url, method, null, body)
const cacheKey = `api:${lookupQuery}:${JSON.stringify(filters ?? null)}`
return cachedLoader(cacheKey, async () => {
const response = await dynamicFetch(url, method, null, resolvedBody)
let { data } = response
if (!data) {
return
}
if (!Array.isArray(data)) {
data = [data]
}
if (!data) return []
if (!Array.isArray(data)) data = [data]
return data.map((a: any) => ({
key: eval(keySelector),
name: eval(nameSelector),
group: eval(groupSelector),
}))
} catch {
return
}
}).catch(() => [] as any)
},
})
}
@ -108,13 +136,11 @@ export const useLookupDataSource = ({
const getLookupDataSource = useCallback(
(options: any, colData: any, data: any) => {
const { lookupDto } = colData
const filters = []
const filters: any[] = []
//Eğer cascadeParentFields varsa ve data yoksa boş döneriz.
// Eğer cascadeParentFields varsa ve row verisi (data) yoksa boş döneriz
if (lookupDto.cascadeParentFields && !data) {
return {
store: [],
}
return { store: [] }
}
if (lookupDto.cascadeParentFields) {
@ -132,9 +158,11 @@ export const useLookupDataSource = ({
}
if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.StaticData) {
const staticKey = `static:${listFormCode}:${colData.fieldName}`
return createLookupStaticDataSource(
() => JSON.parse(lookupDto?.lookupQuery),
filters.length ? filters : null,
staticKey,
)
} else if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.Query) {
return createLookupQueryDataSource(listFormCode, colData.fieldName, filters, isSubForm)
@ -147,9 +175,7 @@ export const useLookupDataSource = ({
isSubForm,
)
} else {
return {
store: [],
}
return { store: [] }
}
},
[listFormCode, isSubForm],
@ -157,3 +183,9 @@ export const useLookupDataSource = ({
return { getLookupDataSource }
}
/** Opsiyonel: dışarıdan cache temizlemek istersen export et */
export const clearLookupCache = (key?: string) => {
if (!key) __lookupCache.clear()
else __lookupCache.delete(key)
}

View file

@ -1,182 +1,19 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { EditingFormItemDto, GridDto, PlatformEditorTypes } from '@/proxy/form/models'
import { captionize } from 'devextreme/core/utils/inflector'
import { useCallback, useEffect, useState } from 'react'
import { GridDto } from '@/proxy/form/models'
import { useListFormCustomDataSource } from '@/shared/useListFormCustomDataSource'
import { Button, Pagination, Select } from '@/components/ui'
import classNames from 'classnames'
import { FaCog, FaSearch } from 'react-icons/fa'
import FormDevExpress from '../form/FormDevExpress'
import { GroupItem } from 'devextreme/ui/form'
import { Form as FormDx } from 'devextreme-react/form'
import FormButtons from '../form/FormButtons'
import { useNavigate } from 'react-router-dom'
import { ROUTES_ENUM } from '@/routes/route.constant'
import CustomStore from 'devextreme/data/custom_store'
import { PermissionResults, SimpleItemWithColData } from '../form/types'
import { usePermission } from '@/utils/hooks/usePermission'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { useLookupDataSource } from '../form/useLookupDataSource'
import { Container, Loading } from '@/components/shared'
import WidgetGroup from '@/components/common/WidgetGroup'
import { GridExtraFilterState } from './Utils'
import { useStoreActions, useStoreState } from '@/store/store'
import { usePWA } from '@/utils/hooks/usePWA'
const CardItem = ({
isSubForm,
row,
gridDto,
listFormCode,
dataSource,
refreshData,
getCachedLookupDataSource,
}: {
isSubForm?: boolean
row: any
gridDto: GridDto
listFormCode: string
dataSource: CustomStore
refreshData: () => void
getCachedLookupDataSource: (editorOptions: any, colData: any, row?: any) => any
}) => {
const [formData, setFormData] = useState(row)
const refForm = useRef<FormDx>(null)
const navigate = useNavigate()
const { translate } = useLocalization()
const { checkPermission } = usePermission()
const isPwaMode = usePWA()
const keyField = gridDto.gridOptions.keyFieldName
const rowId = row[keyField!]
// Form Items
const formItems: GroupItem[] = useMemo(() => {
if (!gridDto) return []
return gridDto.gridOptions.editingFormDto
?.sort((a, b) => (a.order >= b.order ? 1 : -1))
.map((e) => {
return {
itemType: e.itemType,
colCount: e.colCount,
colSpan: e.colSpan,
caption: e.caption,
items: e.items
?.sort((a, b) => (a.order >= b.order ? 1 : -1))
.map((i: EditingFormItemDto) => {
let editorOptions = {}
const colData = gridDto.columnFormats.find((x) => x.fieldName === i.dataField)
try {
editorOptions = i.editorOptions && JSON.parse(i.editorOptions)
} catch {}
const item: SimpleItemWithColData = {
canRead: colData?.canRead ?? false,
canUpdate: colData?.canUpdate ?? false,
canCreate: colData?.canCreate ?? false,
canExport: colData?.canExport ?? false,
dataField: i.dataField,
name: i.dataField,
editorType2: i.editorType2,
editorType:
i.editorType2 == PlatformEditorTypes.dxGridBox ? 'dxDropDownBox' : i.editorType2,
colSpan: i.colSpan,
isRequired: i.isRequired,
editorOptions: {
...editorOptions,
...(colData?.lookupDto?.dataSourceType
? {
dataSource: getCachedLookupDataSource(colData?.editorOptions, colData),
valueExpr: colData?.lookupDto?.valueExpr?.toLowerCase(),
displayExpr: colData?.lookupDto?.displayExpr?.toLowerCase(),
}
: {}),
},
colData,
tagBoxOptions: i.tagBoxOptions,
gridBoxOptions: i.gridBoxOptions,
script: i.script,
}
if (i.dataField.indexOf(':') >= 0) {
item.label = { text: captionize(i.dataField.split(':')[1]) }
}
item.editorOptions = {
...item.editorOptions,
readOnly: true,
}
return item
}),
} as GroupItem
})
}, [gridDto, getCachedLookupDataSource])
const permissionResults: PermissionResults = {
c:
gridDto?.gridOptions.editingOptionDto.allowAdding === true &&
checkPermission(gridDto?.gridOptions.permissionDto.c),
r: checkPermission(gridDto?.gridOptions.permissionDto.r),
u:
gridDto?.gridOptions.editingOptionDto.allowUpdating === true &&
checkPermission(gridDto?.gridOptions.permissionDto.u),
d:
gridDto?.gridOptions.editingOptionDto.allowDeleting === true &&
checkPermission(gridDto?.gridOptions.permissionDto.d),
e: checkPermission(gridDto?.gridOptions.permissionDto.e),
i: checkPermission(gridDto?.gridOptions.permissionDto.i),
}
const onActionEdit = () => {
navigate(
ROUTES_ENUM.protected.admin.formEdit
.replace(':listFormCode', listFormCode)
.replace(':id', rowId!),
)
}
const onActionNew = () => {
navigate(ROUTES_ENUM.protected.admin.formNew.replace(':listFormCode', listFormCode))
}
return (
<div className="bg-white dark:bg-neutral-800 border dark:border-neutral-700 flex flex-col">
<div
className={`flex items-center pt-2 px-2 ${isSubForm ? 'justify-end' : 'justify-between'}`}
>
{!isSubForm && <h3>{translate('::' + gridDto?.gridOptions.title)}</h3>}
{permissionResults && (
<FormButtons
isSubForm={true}
mode="view"
listFormCode={listFormCode}
id={rowId}
gridDto={gridDto}
commandColumnData={{ buttons: [] }}
dataSource={dataSource}
permissions={permissionResults}
handleSubmit={() => ({})}
refreshData={refreshData}
getSelectedRowKeys={() => [rowId]}
getSelectedRowsData={() => [row]}
getFilter={() => null}
onActionEdit={onActionEdit}
onActionNew={onActionNew}
/>
)}
</div>
<div className="flex-grow">
<FormDevExpress
mode="view"
refForm={refForm}
formData={formData}
formItems={formItems}
setFormData={setFormData}
listFormCode={listFormCode}
/>
</div>
</div>
)
}
import CardItem from './CardItem'
interface CardProps {
listFormCode: string
@ -218,20 +55,6 @@ const Card = (props: CardProps) => {
searchParams ? new URLSearchParams(searchParams) : new URLSearchParams(),
)
const lookupCache = useRef<Map<string, any>>(new Map())
const getCachedLookupDataSource = useCallback(
(editorOptions: any, colData: any, row?: any) => {
const key = colData?.fieldName
if (!key) return null
if (!lookupCache.current.has(key)) {
lookupCache.current.set(key, getLookupDataSource(editorOptions, colData, row))
}
return lookupCache.current.get(key)
},
[getLookupDataSource],
)
const onPageSizeSelect = ({ value }: Option) => {
setPageSize(value)
setCurrentPage(1)
@ -499,7 +322,7 @@ const Card = (props: CardProps) => {
listFormCode={listFormCode}
dataSource={gridDataSource}
refreshData={loadData}
getCachedLookupDataSource={getCachedLookupDataSource}
getCachedLookupDataSource={getLookupDataSource}
/>
)
})}

View file

@ -0,0 +1,172 @@
import { EditingFormItemDto, GridDto, PlatformEditorTypes } from "@/proxy/form/models"
import { useLocalization } from "@/utils/hooks/useLocalization"
import { usePermission } from "@/utils/hooks/usePermission"
import { usePWA } from "@/utils/hooks/usePWA"
import CustomStore from "devextreme/data/custom_store"
import { useMemo, useRef, useState } from "react"
import { useNavigate } from "react-router-dom"
import { Form as FormDx } from 'devextreme-react/form'
import { GroupItem } from 'devextreme/ui/form'
import { PermissionResults, SimpleItemWithColData } from "../form/types"
import { captionize } from 'devextreme/core/utils/inflector'
import { ROUTES_ENUM } from "@/routes/route.constant"
import FormButtons from "../form/FormButtons"
import FormDevExpress from "../form/FormDevExpress"
const CardItem = ({
isSubForm,
row,
gridDto,
listFormCode,
dataSource,
refreshData,
getCachedLookupDataSource,
}: {
isSubForm?: boolean
row: any
gridDto: GridDto
listFormCode: string
dataSource: CustomStore
refreshData: () => void
getCachedLookupDataSource: (editorOptions: any, colData: any, row?: any) => any
}) => {
const [formData, setFormData] = useState(row)
const refForm = useRef<FormDx>(null)
const navigate = useNavigate()
const { translate } = useLocalization()
const { checkPermission } = usePermission()
const isPwaMode = usePWA()
const keyField = gridDto.gridOptions.keyFieldName
const rowId = row[keyField!]
// Form Items
const formItems: GroupItem[] = useMemo(() => {
if (!gridDto) return []
return gridDto.gridOptions.editingFormDto
?.sort((a, b) => (a.order >= b.order ? 1 : -1))
.map((e) => {
return {
itemType: e.itemType,
colCount: e.colCount,
colSpan: e.colSpan,
caption: e.caption,
items: e.items
?.sort((a, b) => (a.order >= b.order ? 1 : -1))
.map((i: EditingFormItemDto) => {
let editorOptions = {}
const colData = gridDto.columnFormats.find((x) => x.fieldName === i.dataField)
try {
editorOptions = i.editorOptions && JSON.parse(i.editorOptions)
} catch {}
const item: SimpleItemWithColData = {
canRead: colData?.canRead ?? false,
canUpdate: colData?.canUpdate ?? false,
canCreate: colData?.canCreate ?? false,
canExport: colData?.canExport ?? false,
dataField: i.dataField,
name: i.dataField,
editorType2: i.editorType2,
editorType:
i.editorType2 == PlatformEditorTypes.dxGridBox ? 'dxDropDownBox' : i.editorType2,
colSpan: i.colSpan,
isRequired: i.isRequired,
editorOptions: {
...editorOptions,
...(colData?.lookupDto?.dataSourceType
? {
dataSource: getCachedLookupDataSource(colData?.editorOptions, colData),
valueExpr: colData?.lookupDto?.valueExpr?.toLowerCase(),
displayExpr: colData?.lookupDto?.displayExpr?.toLowerCase(),
}
: {}),
},
colData,
tagBoxOptions: i.tagBoxOptions,
gridBoxOptions: i.gridBoxOptions,
editorScript: i.editorScript,
}
if (i.dataField.indexOf(':') >= 0) {
item.label = { text: captionize(i.dataField.split(':')[1]) }
}
item.editorOptions = {
...item.editorOptions,
readOnly: true,
}
return item
}),
} as GroupItem
})
}, [gridDto, getCachedLookupDataSource])
const permissionResults: PermissionResults = {
c:
gridDto?.gridOptions.editingOptionDto.allowAdding === true &&
checkPermission(gridDto?.gridOptions.permissionDto.c),
r: checkPermission(gridDto?.gridOptions.permissionDto.r),
u:
gridDto?.gridOptions.editingOptionDto.allowUpdating === true &&
checkPermission(gridDto?.gridOptions.permissionDto.u),
d:
gridDto?.gridOptions.editingOptionDto.allowDeleting === true &&
checkPermission(gridDto?.gridOptions.permissionDto.d),
e: checkPermission(gridDto?.gridOptions.permissionDto.e),
i: checkPermission(gridDto?.gridOptions.permissionDto.i),
}
const onActionEdit = () => {
navigate(
ROUTES_ENUM.protected.admin.formEdit
.replace(':listFormCode', listFormCode)
.replace(':id', rowId!),
)
}
const onActionNew = () => {
navigate(ROUTES_ENUM.protected.admin.formNew.replace(':listFormCode', listFormCode))
}
return (
<div className="bg-white dark:bg-neutral-800 border dark:border-neutral-700 flex flex-col">
<div
className={`flex items-center pt-2 px-2 ${isSubForm ? 'justify-end' : 'justify-between'}`}
>
{!isSubForm && <h3>{translate('::' + gridDto?.gridOptions.title)}</h3>}
{permissionResults && (
<FormButtons
isSubForm={true}
mode="view"
listFormCode={listFormCode}
id={rowId}
gridDto={gridDto}
commandColumnData={{ buttons: [] }}
dataSource={dataSource}
permissions={permissionResults}
handleSubmit={() => ({})}
refreshData={refreshData}
getSelectedRowKeys={() => [rowId]}
getSelectedRowsData={() => [row]}
getFilter={() => null}
onActionEdit={onActionEdit}
onActionNew={onActionNew}
/>
)}
</div>
<div className="flex-grow">
<FormDevExpress
mode="view"
refForm={refForm}
formData={formData}
formItems={formItems}
setFormData={setFormData}
listFormCode={listFormCode}
/>
</div>
</div>
)
}
export default CardItem

View file

@ -718,9 +718,7 @@ const Grid = (props: GridProps) => {
.flatMap((group) => group.items || [])
.find((i) => i.dataField === editor.dataField)
console.log(formItem?.dataField, formItem?.script)
if (formItem?.script) {
if (formItem?.editorScript) {
const prevHandler = editor.editorOptions.onValueChanged // varsa önceki handler'ı sakla
editor.editorOptions.onValueChanged = (e: any) => {
@ -745,7 +743,7 @@ const Grid = (props: GridProps) => {
}
}
eval(formItem.script!)
eval(formItem.editorScript!)
//setFormData({ ...formData, Path: e.value, Authority: e.value })
// editor.component.cellValue(
@ -822,9 +820,9 @@ const Grid = (props: GridProps) => {
const formItem = gridDto.gridOptions.editingFormDto
.flatMap((group) => group.items || [])
.find((i) => i.dataField === e.dataField)
if (formItem?.script) {
if (formItem?.editorScript) {
try {
eval(formItem.script)
eval(formItem.editorScript)
} catch (err) {
console.error('Script exec error', err)
}
@ -946,7 +944,7 @@ const Grid = (props: GridProps) => {
colSpan: i.colSpan,
isRequired: i.isRequired,
editorOptions,
script: i.script,
editorScript: i.editorScript,
}
if (i.dataField.indexOf(':') >= 0) {
item.label = { text: captionize(i.dataField.split(':')[1]) }