From e14d6930c21d2b1c108f16ce675dd05474a95d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96zt=C3=BCrk?= Date: Sun, 21 Sep 2025 23:05:13 +0300 Subject: [PATCH] Form komponenti SelectBox Lookup --- ui/public/version.json | 2 +- ui/src/views/form/FormDevExpress.tsx | 23 ++- ui/src/views/form/FormEdit.tsx | 1 + ui/src/views/form/FormNew.tsx | 1 + ui/src/views/form/FormView.tsx | 1 + .../form/editors/GridBoxEditorComponent.tsx | 24 +-- .../form/editors/TagBoxEditorComponent.tsx | 23 +-- ui/src/views/form/types.ts | 5 +- ui/src/views/form/useGridData.tsx | 2 +- ui/src/views/form/useLookupDataSource.ts | 153 ++++++++++++++++++ ui/src/views/list/Card.tsx | 80 ++++++--- 11 files changed, 267 insertions(+), 48 deletions(-) create mode 100644 ui/src/views/form/useLookupDataSource.ts diff --git a/ui/public/version.json b/ui/public/version.json index 9bdcd9ea..b3217b15 100644 --- a/ui/public/version.json +++ b/ui/public/version.json @@ -1,5 +1,5 @@ { - "commit": "3f69cc5", + "commit": "a913220", "releases": [ { "version": "1.0.8", diff --git a/ui/src/views/form/FormDevExpress.tsx b/ui/src/views/form/FormDevExpress.tsx index f044567f..aae4aa0b 100644 --- a/ui/src/views/form/FormDevExpress.tsx +++ b/ui/src/views/form/FormDevExpress.tsx @@ -11,8 +11,10 @@ import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent' import { TagBoxEditorComponent } from './editors/TagBoxEditorComponent' import { RowMode, SimpleItemWithColData } from './types' import { PlatformEditorTypes } from '@/proxy/form/models' +import { useLookupDataSource } from './useLookupDataSource' const FormDevExpress = (props: { + listFormCode: string isSubForm?: boolean mode: RowMode refForm: RefObject @@ -20,7 +22,8 @@ const FormDevExpress = (props: { formItems: GroupItem[] setFormData: Dispatch }) => { - const { isSubForm, mode, refForm, formData, formItems, setFormData } = props + const { listFormCode, isSubForm, mode, refForm, formData, formItems, setFormData } = props + const { getLookupDataSource } = useLookupDataSource({ listFormCode, isSubForm }) return (
@@ -66,6 +69,10 @@ const FormDevExpress = (props: { ...formItem.editorOptions, ...(mode === 'view' ? { readOnly: true } : {}), }} + dataSource={getLookupDataSource( + formItem.colData?.editorOptions, + formItem.colData, + )} > )} > @@ -88,6 +95,10 @@ const FormDevExpress = (props: { ...formItem.editorOptions, ...(mode === 'view' ? { readOnly: true } : {}), }} + dataSource={getLookupDataSource( + formItem.colData?.editorOptions, + formItem.colData, + )} > )} > @@ -99,6 +110,16 @@ const FormDevExpress = (props: { {...formItem} editorOptions={{ ...formItem.editorOptions, + ...(formItem.colData?.lookupDto?.dataSourceType + ? { + dataSource: getLookupDataSource( + formItem.colData?.editorOptions, + formItem.colData, + ), + valueExpr: formItem.colData?.lookupDto?.valueExpr, + displayExpr: formItem.colData?.lookupDto?.displayExpr, + } + : {}), ...(mode === 'view' ? { readOnly: true } : { autoFocus: i === 1 }), }} /> diff --git a/ui/src/views/form/FormEdit.tsx b/ui/src/views/form/FormEdit.tsx index ef3a3590..0dbc7b08 100644 --- a/ui/src/views/form/FormEdit.tsx +++ b/ui/src/views/form/FormEdit.tsx @@ -94,6 +94,7 @@ const FormEdit = ( formData={formData} formItems={formItems} setFormData={setFormData} + listFormCode={listFormCode} /> diff --git a/ui/src/views/form/FormNew.tsx b/ui/src/views/form/FormNew.tsx index 7b304298..1c83aeb1 100644 --- a/ui/src/views/form/FormNew.tsx +++ b/ui/src/views/form/FormNew.tsx @@ -179,6 +179,7 @@ const FormNew = ( formData={formData} formItems={formItems} setFormData={setFormData} + listFormCode={listFormCode} /> ) diff --git a/ui/src/views/form/FormView.tsx b/ui/src/views/form/FormView.tsx index 13e5fb6e..8a342d5a 100644 --- a/ui/src/views/form/FormView.tsx +++ b/ui/src/views/form/FormView.tsx @@ -88,6 +88,7 @@ const FormView = ( formData={formData} formItems={formItems} setFormData={() => {}} + listFormCode={listFormCode} /> diff --git a/ui/src/views/form/editors/GridBoxEditorComponent.tsx b/ui/src/views/form/editors/GridBoxEditorComponent.tsx index ec63e106..4af4d785 100644 --- a/ui/src/views/form/editors/GridBoxEditorComponent.tsx +++ b/ui/src/views/form/editors/GridBoxEditorComponent.tsx @@ -1,5 +1,4 @@ -import { GridBoxOptionsDto } from '@/proxy/form/models' -import { GridColumnData } from '@/views/list/GridColumnData' +import { ColumnFormatDto, GridBoxOptionsDto } from '@/proxy/form/models' import { Button } from 'devextreme-react/button' import DataGrid from 'devextreme-react/data-grid' import DropDownBox from 'devextreme-react/drop-down-box' @@ -12,29 +11,32 @@ const GridBoxEditorComponent = ({ onValueChanged, col, editorOptions, + dataSource }: { value: any options?: GridBoxOptionsDto values: any // all row data onValueChanged: (e: any) => void - col?: GridColumnData + col?: ColumnFormatDto editorOptions: any + dataSource: any }): ReactElement => { const gridRef = useRef(null) const val = Array.isArray(value) ? value : [value] - const lookup = col?.lookup - const dataSource: any = - typeof lookup?.dataSource === 'function' - ? lookup.dataSource(values) - : (lookup?.dataSource ?? []) + const lookupDto = col?.lookupDto + + // const dataSource: any = + // typeof lookupDto?.dataSource === 'function' + // ? lookupDto.dataSource(values) + // : (lookupDto?.dataSource ?? []) return ( { @@ -51,7 +53,7 @@ const GridBoxEditorComponent = ({ void - col?: GridColumnData + col?: ColumnFormatDto editorOptions: any + dataSource: any }): ReactElement => { const val = Array.isArray(value) ? value : [value] - const lookup = col?.lookup - const dataSource = - typeof lookup?.dataSource === 'function' - ? lookup.dataSource(values) - : (lookup?.dataSource ?? []) + const lookupDto = col?.lookupDto + + // const dataSource = + // typeof lookup?.dataSource === 'function' + // ? lookup.dataSource(values) + // : (lookup?.dataSource ?? []) return ( x.dataField === i.dataField), + colData: gridDto?.columnFormats.find((x) => x.fieldName === i.dataField), tagBoxOptions: i.tagBoxOptions, gridBoxOptions: i.gridBoxOptions, } diff --git a/ui/src/views/form/useLookupDataSource.ts b/ui/src/views/form/useLookupDataSource.ts new file mode 100644 index 00000000..67511172 --- /dev/null +++ b/ui/src/views/form/useLookupDataSource.ts @@ -0,0 +1,153 @@ +import { dynamicFetch } from '@/services/form.service' +import { UiLookupDataSourceTypeEnum } from '@/proxy/form/models' +import CustomStore from 'devextreme/data/custom_store' +import { useCallback } from 'react' + +const createLookupStaticDataSource = ( + load: () => any, + filter: any = null, + key: any = 'key', + sort: any = 'name', +) => ({ + store: new CustomStore({ + key, + loadMode: 'raw', + load, + }), + sort, + filter, +}) + +const createLookupQueryDataSource = ( + listFormCode?: string, + listFormFieldName?: string, + filters?: any[], + isSubForm?: boolean, +) => { + return new CustomStore({ + loadMode: 'raw', + load: async () => { + if (!isSubForm && listFormCode && !window.location.pathname.includes(listFormCode)) { + return + } + + try { + 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, + })) + } catch (error: any) { + return null + } + }, + }) +} + +const createLookupApiDataSource = ( + listFormCode?: string, + lookupQuery?: string, + filters?: any[], + keyName?: string, + isSubForm?: boolean, +) => { + return new CustomStore({ + key: keyName, + loadMode: 'raw', + load: async () => { + if (!isSubForm && listFormCode && !window.location.pathname.includes(listFormCode)) { + return + } + if (!lookupQuery) { + return + } + + const [method, url, body, keySelector, nameSelector, groupSelector] = lookupQuery.split(';') + + if (filters?.length) { + for (let i = 0; i < filters.length; i++) { + body.replace(`@param${i}`, filters[i]) + } + } + + try { + const response = await dynamicFetch(url, method, null, body) + 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 { + return + } + }, + }) +} + +export const useLookupDataSource = ({ + listFormCode, + isSubForm, +}: { + listFormCode: string + isSubForm?: boolean +}) => { + const getLookupDataSource = useCallback( + (options: any, colData: any) => { + const { lookupDto } = colData + const filters = [] + if (lookupDto.cascadeParentFields) { + if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.StaticData) { + filters.push([ + lookupDto?.cascadeRelationField, + lookupDto?.cascadeFilterOperator, + options?.data[lookupDto?.cascadeParentField], + ]) + } else { + const data = options?.data ?? options + for (const cascadeParentField of lookupDto.cascadeParentFields.split(',')) { + filters.push(data[cascadeParentField]) + } + } + } + + if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.StaticData) { + return createLookupStaticDataSource( + () => JSON.parse(lookupDto?.lookupQuery), + filters.length ? filters : null, + ) + } else if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.Query) { + return createLookupQueryDataSource(listFormCode, colData.fieldName, filters, isSubForm) + } else if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.WebService) { + return createLookupApiDataSource( + listFormCode, + lookupDto?.lookupQuery, + filters, + colData.lookupDto?.valueExpr?.toLowerCase(), + isSubForm, + ) + } else { + return { + store: [], + } + } + }, + [listFormCode, isSubForm], + ) + + return { getLookupDataSource } +} diff --git a/ui/src/views/list/Card.tsx b/ui/src/views/list/Card.tsx index 8577380e..6bc900dc 100644 --- a/ui/src/views/list/Card.tsx +++ b/ui/src/views/list/Card.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { GridDto } from '@/proxy/form/models' +import { EditingFormItemDto, GridDto, PlatformEditorTypes } from '@/proxy/form/models' import { captionize } from 'devextreme/core/utils/inflector' import { useListFormCustomDataSource } from '@/shared/useListFormCustomDataSource' import { Pagination, Select } from '@/components/ui' @@ -7,13 +7,13 @@ import classNames from 'classnames' import { getList } from '@/services/form.service' import { FaInbox } from 'react-icons/fa' import FormDevExpress from '../form/FormDevExpress' -import { GroupItem, SimpleItem } from 'devextreme/ui/form' +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 } from '../form/types' +import { PermissionResults, SimpleItemWithColData } from '../form/types' import { usePermission } from '@/utils/hooks/usePermission' import { useLocalization } from '@/utils/hooks/useLocalization' @@ -54,22 +54,61 @@ const CardItem = ({ const formItems: GroupItem[] = useMemo(() => { if (!gridDto) return [] - const items: SimpleItem[] = gridDto.columnFormats - .filter((col) => col.visible && col.listOrderNo > 0) - .sort((a, b) => a.listOrderNo - b.listOrderNo) - .slice(0, 10) - .map((col) => ({ - dataField: col.fieldName, - label: { text: captionize(col.captionName || col.fieldName!) }, - })) - - return [ - { - itemType: 'group', - colCount: 1, - items: items, - }, - ] + return gridDto?.gridOptions.editingFormDto + ?.sort((a: any, b: any) => { + return a.order >= b.order ? 1 : -1 + }) + .map((e: any) => { + return { + itemType: e.itemType, + colCount: e.colCount, + colSpan: e.colSpan, + caption: e.caption, + items: e.items + ?.sort((a: any, b: any) => { + return a.order >= b.order ? 1 : -1 + }) + .map((i: EditingFormItemDto) => { + let editorOptions = {} + try { + editorOptions = i.editorOptions && JSON.parse(i.editorOptions) + } catch {} + const item: SimpleItemWithColData = { + canRead: + gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.canRead ?? + false, + canUpdate: + gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.canUpdate ?? + false, + canCreate: + gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.canCreate ?? + false, + canExport: + gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.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, + colData: gridDto.columnFormats.find((x) => x.fieldName === i.dataField), + tagBoxOptions: i.tagBoxOptions, + gridBoxOptions: i.gridBoxOptions, + } + if (i.dataField.indexOf(':') >= 0) { + item.label = { text: captionize(i.dataField.split(':')[1]) } + } + item.editorOptions = { + ...item.editorOptions, + readOnly: true, + } + return item + }), + } as GroupItem + }) }, [gridDto]) const permissionResults: PermissionResults = { @@ -130,6 +169,7 @@ const CardItem = ({ formData={formData} formItems={formItems} setFormData={setFormData} + listFormCode={listFormCode} /> @@ -194,7 +234,7 @@ const Card = ({ listFormCode, searchParams }: CardProps) => { const allowedSizes = pagerOptions?.allowedPageSizes ?.split(',') .map((s) => Number(s.trim())) - .filter((n) => !isNaN(n) && n > 0) || [20, 50, 100] + .filter((n) => !isNaN(n) && n > 0) || [10, 20, 50, 100] setPageSizeOptions(allowedSizes.map((size) => ({ value: size, label: `${size} page` }))) }, [gridDto, listFormCode, searchParams])