diff --git a/api/src/Kurs.Platform.Application.Contracts/ListForms/GridOptionsDto/EditingFormDto.cs b/api/src/Kurs.Platform.Application.Contracts/ListForms/GridOptionsDto/EditingFormDto.cs index b4ee940e..d24702ce 100644 --- a/api/src/Kurs.Platform.Application.Contracts/ListForms/GridOptionsDto/EditingFormDto.cs +++ b/api/src/Kurs.Platform.Application.Contracts/ListForms/GridOptionsDto/EditingFormDto.cs @@ -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; } } diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/ListFormsSeeder.cs b/api/src/Kurs.Platform.DbMigrator/Seeds/ListFormsSeeder.cs index dedb202f..593169d4 100644 --- a/api/src/Kurs.Platform.DbMigrator/Seeds/ListFormsSeeder.cs +++ b/api/src/Kurs.Platform.DbMigrator/Seeds/ListFormsSeeder.cs @@ -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}, diff --git a/api/src/Kurs.Platform.Domain.Shared/PlatformConsts.cs b/api/src/Kurs.Platform.Domain.Shared/PlatformConsts.cs index 81756991..be61c74b 100644 --- a/api/src/Kurs.Platform.Domain.Shared/PlatformConsts.cs +++ b/api/src/Kurs.Platform.Domain.Shared/PlatformConsts.cs @@ -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 diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/Seeds/TenantData.json b/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/Seeds/TenantData.json index 6cdc127d..c606792b 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/Seeds/TenantData.json +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/Seeds/TenantData.json @@ -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", diff --git a/ui/src/proxy/form/models.ts b/ui/src/proxy/form/models.ts index a86a4e5f..db7856d4 100644 --- a/ui/src/proxy/form/models.ts +++ b/ui/src/proxy/form/models.ts @@ -416,7 +416,7 @@ export interface EditingFormItemDto { isRequired?: boolean gridBoxOptions?: GridBoxOptionsDto tagBoxOptions?: TagBoxOptionsDto - script?: string + editorScript?: string } export interface GridEditingPopupDto { diff --git a/ui/src/shared/useListFormColumns.ts b/ui/src/shared/useListFormColumns.ts index d86f84ae..c1967d61 100644 --- a/ui/src/shared/useListFormColumns.ts +++ b/ui/src/shared/useListFormColumns.ts @@ -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>() + +const cachedLoader = (key: string, loader: () => Promise) => { + 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 response = await dynamicFetch('list-form-select/lookup', 'POST', null, { - listFormCode, - listFormFieldName, - filters, - }) + 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) => ({ - key: a.Key, - name: a.Name, - group: a.Group, - })) + return (response.data ?? []).map((a: any) => ({ + key: a.Key, + name: a.Name, + group: a.Group, + })) + }) } catch (error: any) { - // toast.push( - // - // Select error - // {error.toString()} - // , - // { - // 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) - let { data } = response - if (!data) { - // toast.push( - // - // {'Lookup Datası boş geldi.'} - // , - // { - // placement: 'top-end', - // }, - // ) - return - } - - if (!Array.isArray(data)) { - data = [data] - } - - return data.map((a: any) => ({ - key: eval(keySelector), - name: eval(nameSelector), - group: eval(groupSelector), - })) + 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] + return data.map((a: any) => ({ + key: eval(keySelector), + name: eval(nameSelector), + group: eval(groupSelector), + })) + }) } catch { - // toast.push( - // - // {'Network error'} - // , - // { - // placement: 'top-end', - // }, - // ) return } }, diff --git a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogEditForm.tsx b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogEditForm.tsx index 22a350ca..4cd61437 100644 --- a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogEditForm.tsx +++ b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogEditForm.tsx @@ -740,9 +740,9 @@ function JsonRowOpDialogEditForm({
diff --git a/ui/src/views/form/FormButtons.tsx b/ui/src/views/form/FormButtons.tsx index 8af19e6f..0bff0f70 100644 --- a/ui/src/views/form/FormButtons.tsx +++ b/ui/src/views/form/FormButtons.tsx @@ -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 })} diff --git a/ui/src/views/form/FormDevExpress.tsx b/ui/src/views/form/FormDevExpress.tsx index 0430a354..716c8c46 100644 --- a/ui/src/views/form/FormDevExpress.tsx +++ b/ui/src/views/form/FormDevExpress.tsx @@ -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) } diff --git a/ui/src/views/form/types.ts b/ui/src/views/form/types.ts index e5c2f602..ff7abb64 100644 --- a/ui/src/views/form/types.ts +++ b/ui/src/views/form/types.ts @@ -20,7 +20,7 @@ export type SimpleItemWithColData = Overwrite< canUpdate: boolean canExport: boolean editorType2: EditorType2 - script?: string + editorScript?: string } > export type RowMode = 'view' | 'edit' | 'new' diff --git a/ui/src/views/form/useGridData.tsx b/ui/src/views/form/useGridData.tsx index 2e264358..066ecc3a 100644 --- a/ui/src/views/form/useGridData.tsx +++ b/ui/src/views/form/useGridData.tsx @@ -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]) } diff --git a/ui/src/views/form/useLookupDataSource.ts b/ui/src/views/form/useLookupDataSource.ts index 35b578cf..ba3ad719 100644 --- a/ui/src/views/form/useLookupDataSource.ts +++ b/ui/src/views/form/useLookupDataSource.ts @@ -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 + * - Aynı anda açılan editörler tek ağ çağrısı üzerinden paylaşır + * - Hata olursa cache temizlenir (yeniden denenebilir) + */ +const __lookupCache = new Map>() + +const cachedLoader = (key: string, loader: () => Promise) => { + 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) }, }) } @@ -106,15 +134,13 @@ export const useLookupDataSource = ({ isSubForm?: boolean }) => { const getLookupDataSource = useCallback( - (options: any, colData: any, data: any) => { + (options: any, colData: any, data: any) => { const { lookupDto } = colData - const filters = [] - - //Eğer cascadeParentFields varsa ve data yoksa boş döneriz. + const filters: any[] = [] + + // 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) +} diff --git a/ui/src/views/list/Card.tsx b/ui/src/views/list/Card.tsx index 2d9587d0..6408d78f 100644 --- a/ui/src/views/list/Card.tsx +++ b/ui/src/views/list/Card.tsx @@ -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(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 ( -
-
- {!isSubForm &&

{translate('::' + gridDto?.gridOptions.title)}

} - {permissionResults && ( - ({})} - refreshData={refreshData} - getSelectedRowKeys={() => [rowId]} - getSelectedRowsData={() => [row]} - getFilter={() => null} - onActionEdit={onActionEdit} - onActionNew={onActionNew} - /> - )} -
-
- -
-
- ) -} +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>(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} /> ) })} diff --git a/ui/src/views/list/CardItem.tsx b/ui/src/views/list/CardItem.tsx new file mode 100644 index 00000000..05b02a23 --- /dev/null +++ b/ui/src/views/list/CardItem.tsx @@ -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(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 ( +
+
+ {!isSubForm &&

{translate('::' + gridDto?.gridOptions.title)}

} + {permissionResults && ( + ({})} + refreshData={refreshData} + getSelectedRowKeys={() => [rowId]} + getSelectedRowsData={() => [row]} + getFilter={() => null} + onActionEdit={onActionEdit} + onActionNew={onActionNew} + /> + )} +
+
+ +
+
+ ) +} + +export default CardItem \ No newline at end of file diff --git a/ui/src/views/list/Grid.tsx b/ui/src/views/list/Grid.tsx index dc38968d..9e6bfa49 100644 --- a/ui/src/views/list/Grid.tsx +++ b/ui/src/views/list/Grid.tsx @@ -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]) }