diff --git a/ui/src/views/admin/listForm/edit/json-row-operations/EditorScriptBuilderDialog.tsx b/ui/src/views/admin/listForm/edit/json-row-operations/EditorScriptBuilderDialog.tsx index 83bff7a..d84e0c5 100644 --- a/ui/src/views/admin/listForm/edit/json-row-operations/EditorScriptBuilderDialog.tsx +++ b/ui/src/views/admin/listForm/edit/json-row-operations/EditorScriptBuilderDialog.tsx @@ -2,7 +2,7 @@ import { Button, Dialog } from '@/components/ui' import { SelectBoxOption } from '@/types/shared' import Editor from '@monaco-editor/react' import { useEffect, useMemo, useState } from 'react' -import { FaBolt, FaCheck, FaCode, FaPlus, FaTimes, FaTrash } from 'react-icons/fa' +import { FaBolt, FaCheck, FaCode, FaMagic, FaPlus, FaTimes, FaTrash } from 'react-icons/fa' type CopyMapping = { id: string @@ -30,8 +30,10 @@ type ConditionalAction = { | 'alert' | 'confirm' | 'calculate' + | 'setReadOnly' targetField: string textValue: string + booleanValue: 'true' | 'false' apiUrl: string apiMethod: 'GET' | 'POST' responsePath: string @@ -60,6 +62,35 @@ const actionIconCellClass = const makeId = () => `${Date.now()}_${Math.random().toString(36).slice(2)}` +const formatScriptFallback = (source: string) => { + const trimmed = source.trim() + if (!trimmed) return '' + + let indent = 0 + return trimmed + .replace(/\s*;\s*/g, ';\n') + .replace(/\s*\{\s*/g, ' {\n') + .replace(/\s*\}\s*/g, '\n}\n') + .replace(/\n\s*\n/g, '\n') + .split('\n') + .map((line) => line.trim()) + .filter(Boolean) + .map((line) => { + if (line.startsWith('}')) { + indent = Math.max(0, indent - 1) + } + + const formattedLine = `${' '.repeat(indent)}${line}` + + if (line.endsWith('{')) { + indent += 1 + } + + return formattedLine + }) + .join('\n') +} + const createConditionalAction = (): ConditionalAction => ({ id: makeId(), source: '', @@ -68,6 +99,7 @@ const createConditionalAction = (): ConditionalAction => ({ actionType: 'setField', targetField: '', textValue: '', + booleanValue: 'true', apiUrl: '', apiMethod: 'GET', responsePath: '', @@ -124,6 +156,7 @@ function isConditionalActionReady(action: ConditionalAction) { if (action.actionType === 'alert' || action.actionType === 'confirm') return Boolean(action.message.trim()) if (action.actionType === 'calculate') return Boolean(action.targetField && action.formula.trim()) + if (action.actionType === 'setReadOnly') return Boolean(action.targetField) return false } @@ -233,6 +266,9 @@ function buildScript({ const needsRollbackCurrentValue = activeConditionalActions.some( (action) => action.actionType === 'confirm', ) + const needsSetEditorReadOnly = activeConditionalActions.some( + (action) => action.actionType === 'setReadOnly', + ) const lines: string[] = ['(async () => {'] @@ -269,6 +305,12 @@ function buildScript({ ) } + if (needsSetEditorReadOnly) { + lines.push( + ' const setEditorReadOnly = (field, readOnly) => { if (typeof runtimeSetEditorReadOnly === "function") runtimeSetEditorReadOnly(field, readOnly); };', + ) + } + activeCopyMappings.forEach((mapping) => { const source = mapping.source.includes('.') ? `getByPath(selectedItem, ${quote(mapping.source)})` @@ -345,6 +387,16 @@ function buildScript({ activeConditionalActions.forEach((action) => { const condition = buildGenericCondition(action) + + if (action.actionType === 'setReadOnly' && action.targetField) { + const enabledValue = action.booleanValue === 'true' + const readOnlyExpression = enabledValue ? condition : `!(${condition})` + lines.push( + ` setEditorReadOnly(${quote(action.targetField)}, ${readOnlyExpression});`, + ) + return + } + lines.push(` if (${condition}) {`) if (action.actionType === 'setField' && action.targetField) { @@ -467,6 +519,10 @@ function EditorScriptBuilderDialog({ const findField = (fieldName: string) => availableFields.find((field) => field.toLowerCase() === fieldName.toLowerCase()) || '' + const formatScriptPreview = () => { + setScriptEditorValue((current) => formatScriptFallback(current)) + } + const fillAmountDefaults = () => { setAmountQuantityField((current) => current || findField('Quantity')) setAmountUnitPriceField((current) => current || findField('UnitPrice')) @@ -582,14 +638,19 @@ function EditorScriptBuilderDialog({ ], ) + const formattedGeneratedScript = useMemo( + () => formatScriptFallback(generatedScript), + [generatedScript], + ) + useEffect(() => { if (!isOpen) return setScriptEditorValue((current) => { - if (current === lastGeneratedScript || !current.trim()) return generatedScript + if (current === lastGeneratedScript || !current.trim()) return formattedGeneratedScript return current }) - setLastGeneratedScript(generatedScript) - }, [generatedScript, isOpen, lastGeneratedScript]) + setLastGeneratedScript(formattedGeneratedScript) + }, [formattedGeneratedScript, isOpen, lastGeneratedScript]) const renderFieldSelect = (value: string, onChange: (value: string) => void) => ( @@ -896,6 +961,7 @@ function EditorScriptBuilderDialog({ 'setField', 'apiToField', 'calculate', + 'setReadOnly', ].includes(action.actionType) && (
{renderLabeledFieldSelect('Hedef field', action.targetField, (value) => @@ -919,6 +985,26 @@ function EditorScriptBuilderDialog({ )} + {action.actionType === 'setReadOnly' && ( + + )} + {action.actionType === 'apiToField' && ( <>
+ items.flatMap((item) => [ + ...(item?.dataField ? [item] : []), + ...flattenFormItems(item?.items || []), + ...(item?.tabs || []).flatMap((tab: any) => flattenFormItems(tab?.items || [])), + ]) + +const updateReadOnlyInFormItems = (items: any[] = [], field: string, readOnly: boolean) => { + let changed = false + const expected = String(field || '').toLowerCase() + + const nextItems = items.map((item) => { + const key = item?.dataField || item?.name + let nextItem = item + + if (key && String(key).toLowerCase() === expected) { + const editorOptions = nextItem.editorOptions || {} + if (editorOptions.readOnly !== readOnly) { + changed = true + nextItem = { + ...nextItem, + editorOptions: { + ...editorOptions, + readOnly, + }, + } + } + } + + if (nextItem?.items?.length) { + const childResult = updateReadOnlyInFormItems(nextItem.items, field, readOnly) + if (childResult.changed) { + changed = true + nextItem = { ...nextItem, items: childResult.items } + } + } + + if (nextItem?.tabs?.length) { + const tabs = nextItem.tabs.map((tab: any) => { + const tabResult = updateReadOnlyInFormItems(tab.items, field, readOnly) + if (tabResult.changed) { + changed = true + return { ...tab, items: tabResult.items } + } + return tab + }) + nextItem = tabs === nextItem.tabs ? nextItem : { ...nextItem, tabs } + } + + return nextItem + }) + + return { items: nextItems, changed } +} + +const findFormFieldKey = (items: any[] = [], field: string): string => { + const expected = String(field || '').toLowerCase() + + for (const item of items || []) { + const key = item?.dataField || item?.name + if (key && String(key).toLowerCase() === expected) { + return key + } + + const childKey = findFormFieldKey(item?.items || [], field) + if (childKey) { + return childKey + } + + for (const tab of item?.tabs || []) { + const tabKey = findFormFieldKey(tab?.items || [], field) + if (tabKey) { + return tabKey + } + } + } + + return field +} + +const setFormEditorReadOnly = (form: any, field: string, readOnly: boolean) => { + if (!form?.option) return false + + const apply = () => { + const formItems = form.option('items') || [] + const resolvedField = findFormFieldKey(formItems, field) + const editor = form.getEditor?.(resolvedField) ?? form.getEditor?.(field) + const result = updateReadOnlyInFormItems(formItems, resolvedField, readOnly) + + if (result.changed) { + try { + const item = form.itemOption?.(resolvedField) ?? form.itemOption?.(field) + if (item) { + form.itemOption?.(resolvedField, 'editorOptions', { + ...(item.editorOptions || {}), + readOnly, + }) + } else { + form.option('items', result.items) + } + } catch { + form.option('items', result.items) + } + } + + const activeEditor = editor ?? form.getEditor?.(resolvedField) ?? form.getEditor?.(field) + if (activeEditor?.option?.('readOnly') !== readOnly) { + activeEditor?.option?.('readOnly', readOnly) + } + } + + apply() + return true +} + +const getValueByField = (data: Record = {}, field?: string) => { + if (!field) return undefined + if (Object.prototype.hasOwnProperty.call(data, field)) return data[field] + const key = Object.keys(data).find( + (itemKey) => itemKey.toLowerCase() === String(field).toLowerCase(), + ) + return key ? data[key] : undefined +} + +const shouldRunEditorScriptOnContentReady = (script?: string) => + Boolean(script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly'))) + const FormDevExpress = (props: { listFormCode: string isSubForm?: boolean @@ -28,6 +155,7 @@ const FormDevExpress = (props: { const formDataRef = useRef(formData) const formItemsRef = useRef(formItems) + const formInstanceRef = useRef() useEffect(() => { formDataRef.current = formData @@ -41,7 +169,7 @@ const FormDevExpress = (props: { useEffect(() => { if (!refForm.current?.instance()) return - const allItems = formItems.flatMap((group) => (group.items as SimpleItemWithColData[]) || []) + const allItems = formItems.flatMap((group) => flattenFormItems([group])) allItems.forEach((item) => { if (item.colData?.lookupDto?.dataSourceType && item.editorOptions?.dataSource) { @@ -62,9 +190,7 @@ const FormDevExpress = (props: { const updateCascadeDisabledStates = () => { if (!refForm.current?.instance()) return - const allItems = formItemsRef.current.flatMap( - (group) => (group.items as SimpleItemWithColData[]) || [], - ) + const allItems = formItemsRef.current.flatMap((group) => flattenFormItems([group])) allItems.forEach((item) => { const cascadeParentFields = item.colData?.lookupDto?.cascadeParentFields @@ -107,9 +233,7 @@ const FormDevExpress = (props: { const newFormData = { ...formData, [e.dataField!]: e.value } // Cascading child field'leri temizle (parent field değiştiğinde) - const allItems = formItemsRef.current.flatMap( - (group) => (group.items as SimpleItemWithColData[]) || [], - ) + const allItems = formItemsRef.current.flatMap((group) => flattenFormItems([group])) const cascadingChildren = allItems.filter((item) => { const parentFields = item.colData?.lookupDto?.cascadeParentFields?.split(',') || [] return parentFields.some((field) => field.trim() === e.dataField) @@ -129,11 +253,24 @@ const FormDevExpress = (props: { //Dinamik script const changeItem = formItemsRef.current - .flatMap((group) => (group.items as SimpleItemWithColData[]) || []) - .find((i: SimpleItemWithColData) => i.dataField === e.dataField) + .flatMap((group) => flattenFormItems([group])) + .find( + (i: SimpleItemWithColData) => + String(i.dataField || '').toLowerCase() === String(e.dataField || '').toLowerCase(), + ) if (changeItem?.editorScript) { try { + const form = e.component + const editor = { + dataField: changeItem.dataField, + component: form, + } + const formData = newFormData + formDataRef.current = newFormData + const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) => + setFormEditorReadOnly(formInstanceRef.current ?? form, field, readOnly) + //setFormData({...formData, Path: e.value}); //UiEvalService.ApiGenerateBackgroundWorkers(); //setFormData({ ...formData, Path: (v => v === '1' ? '1-deneme' : v === '0' ? '0-deneme' : '')(e.value) }) @@ -142,8 +279,47 @@ const FormDevExpress = (props: { console.error('Script execution failed for', changeItem.name, err) } } + }} onContentReady={(e) => { + formInstanceRef.current = e.component + + const form = e.component + const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) => + setFormEditorReadOnly(form, field, readOnly) + + const runReadOnlyScripts = () => { + const currentFormData = { + ...(formDataRef.current || {}), + ...(form?.option?.('formData') || {}), + } + formDataRef.current = currentFormData + + formItemsRef.current + .flatMap((group) => flattenFormItems([group])) + .filter((formItem) => shouldRunEditorScriptOnContentReady(formItem.editorScript)) + .forEach((formItem) => { + try { + const editor = { + dataField: formItem.dataField, + component: form, + } + const formData = currentFormData + const e = { + component: form, + dataField: formItem.dataField, + value: getValueByField(currentFormData, formItem.dataField), + } + + eval(formItem.editorScript!) + } catch (err) { + console.error('Script execution failed on contentReady for', formItem.name, err) + } + }) + } + + runReadOnlyScripts() + const groupItems = e.component.option('items') as any[] const firstItem = groupItems?.[0]?.items?.[0] diff --git a/ui/src/views/list/Grid.tsx b/ui/src/views/list/Grid.tsx index 52c5180..cee682e 100644 --- a/ui/src/views/list/Grid.tsx +++ b/ui/src/views/list/Grid.tsx @@ -106,6 +106,138 @@ const getPersistedInsertedKey = ( return undefined } +const updateReadOnlyInFormItems = (items: any[] = [], field: string, readOnly: boolean) => { + let changed = false + const expected = String(field || '').toLowerCase() + + const nextItems = items.map((item) => { + const key = item?.dataField || item?.name + let nextItem = item + + if (key && String(key).toLowerCase() === expected) { + const editorOptions = nextItem.editorOptions || {} + if (editorOptions.readOnly !== readOnly) { + changed = true + nextItem = { + ...nextItem, + editorOptions: { + ...editorOptions, + readOnly, + }, + } + } + } + + if (nextItem?.items?.length) { + const childResult = updateReadOnlyInFormItems(nextItem.items, field, readOnly) + if (childResult.changed) { + changed = true + nextItem = { ...nextItem, items: childResult.items } + } + } + + if (nextItem?.tabs?.length) { + const tabs = nextItem.tabs.map((tab: any) => { + const tabResult = updateReadOnlyInFormItems(tab.items, field, readOnly) + if (tabResult.changed) { + changed = true + return { ...tab, items: tabResult.items } + } + return tab + }) + nextItem = tabs === nextItem.tabs ? nextItem : { ...nextItem, tabs } + } + + return nextItem + }) + + return { items: nextItems, changed } +} + +const findFormFieldKey = (items: any[] = [], field: string): string => { + const expected = String(field || '').toLowerCase() + + for (const item of items || []) { + const key = item?.dataField || item?.name + if (key && String(key).toLowerCase() === expected) { + return key + } + + const childKey = findFormFieldKey(item?.items || [], field) + if (childKey) { + return childKey + } + + for (const tab of item?.tabs || []) { + const tabKey = findFormFieldKey(tab?.items || [], field) + if (tabKey) { + return tabKey + } + } + } + + return field +} + +const setFormEditorReadOnly = (form: any, field: string, readOnly: boolean) => { + if (!form?.option) return false + + const apply = () => { + const formItems = form.option('items') || [] + const resolvedField = findFormFieldKey(formItems, field) + const editor = form.getEditor?.(resolvedField) ?? form.getEditor?.(field) + const result = updateReadOnlyInFormItems(formItems, resolvedField, readOnly) + + if (result.changed) { + try { + const item = form.itemOption?.(resolvedField) ?? form.itemOption?.(field) + if (item) { + form.itemOption?.(resolvedField, 'editorOptions', { + ...(item.editorOptions || {}), + readOnly, + }) + } else { + form.option('items', result.items) + } + } catch { + form.option('items', result.items) + } + } + + const activeEditor = editor ?? form.getEditor?.(resolvedField) ?? form.getEditor?.(field) + if (activeEditor?.option?.('readOnly') !== readOnly) { + activeEditor?.option?.('readOnly', readOnly) + } + } + + apply() + return true +} + +const getActiveEditingForm = (component: any) => { + const editForm = component?.getView?.('editingView')?._editForm + return editForm?.getEditor || editForm?.itemOption ? editForm : undefined +} + +const flattenEditingFormItems = (items: any[] = []): EditingFormItemDto[] => + items.flatMap((item) => [ + ...(item?.dataField ? [item] : []), + ...flattenEditingFormItems(item?.items || []), + ...(item?.tabs || []).flatMap((tab: any) => flattenEditingFormItems(tab?.items || [])), + ]) + +const getValueByField = (data: Record = {}, field?: string) => { + if (!field) return undefined + if (Object.prototype.hasOwnProperty.call(data, field)) return data[field] + const key = Object.keys(data).find( + (itemKey) => itemKey.toLowerCase() === String(field).toLowerCase(), + ) + return key ? data[key] : undefined +} + +const shouldRunEditorScriptOnContentReady = (script?: string) => + Boolean(script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly'))) + const Grid = (props: GridProps) => { const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props const { translate } = useLocalization() @@ -116,6 +248,7 @@ const Grid = (props: GridProps) => { const refListFormCode = useRef('') const widgetGroupRef = useRef(null) const editingFormDataRef = useRef>({}) + const editingFormInstanceRef = useRef() // Edit popup state kaydetmeyi engellemek için flag const isEditingRef = useRef(false) @@ -571,7 +704,7 @@ const Grid = (props: GridProps) => { (editor: DataGridTypes.EditorPreparingEvent) => { if (editor.parentType === 'dataRow' && editor.dataField && gridDto) { const formItem = gridDto.gridOptions.editingFormDto - .flatMap((group) => group.items || []) + .flatMap((group) => flattenEditingFormItems([group])) .find((i) => i.dataField === editor.dataField) const fieldName = editor.dataField.split(':')[0] const columnFormat = gridDto.columnFormats.find((column) => column.fieldName === fieldName) @@ -632,7 +765,7 @@ const Grid = (props: GridProps) => { ) try { const childFormItem = gridDto.gridOptions.editingFormDto - .flatMap((group) => group.items || []) + .flatMap((group) => flattenEditingFormItems([group])) .find((i) => i.dataField === childFieldName) const childEditorOptions = childFormItem?.editorOptions ? JSON.parse(childFormItem.editorOptions) @@ -665,6 +798,7 @@ const Grid = (props: GridProps) => { if (formItem?.editorScript) { const prevHandler = editor.editorOptions.onValueChanged + const editorDataField = editor.dataField editor.editorOptions.onValueChanged = (e: any) => { if (prevHandler) prevHandler(e) @@ -674,8 +808,16 @@ const Grid = (props: GridProps) => { const rowKey = grid.option('editing.editRowKey') const rowIndex = grid.getRowIndexByKey(rowKey) - const formData = grid.getVisibleRows().find((r) => r.key === rowKey)?.data || {} - + const formData = { + ...(grid.getVisibleRows().find((r) => r.key === rowKey)?.data || {}), + [editorDataField]: e.value, + } + const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) => + setFormEditorReadOnly( + editingFormInstanceRef.current ?? getActiveEditingForm(grid), + field, + readOnly, + ) const setFormData = (newData: any) => { if (rowIndex >= 0) { Object.keys(newData).forEach((field) => { @@ -1388,6 +1530,58 @@ const Grid = (props: GridProps) => { }} form={{ colCount: 1, + onContentReady: (e) => { + editingFormInstanceRef.current = e.component + + const form = e.component + const grid = gridRef.current?.instance() + const rowKey = grid?.option('editing.editRowKey') + const rowIndex = rowKey !== undefined ? grid?.getRowIndexByKey(rowKey) : -1 + const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) => + setFormEditorReadOnly(form, field, readOnly) + const setFormData = (newData: any) => { + form?.option?.('formData', newData) + editingFormDataRef.current = { ...newData } + if (grid && rowIndex !== undefined && rowIndex >= 0) { + Object.keys(newData).forEach((field) => { + grid.cellValue(rowIndex, field, newData[field]) + }) + } + } + + const runReadOnlyScripts = () => { + const formData = { + ...editingFormDataRef.current, + ...(form?.option?.('formData') || {}), + } + editingFormDataRef.current = { ...formData } + + gridDto.gridOptions.editingFormDto + .flatMap((group) => flattenEditingFormItems([group])) + .filter((formItem) => + shouldRunEditorScriptOnContentReady(formItem.editorScript), + ) + .forEach((formItem) => { + try { + const editor = { + dataField: formItem.dataField, + component: grid, + } + const e = { + component: form, + dataField: formItem.dataField, + value: getValueByField(formData, formItem.dataField), + } + + eval(formItem.editorScript!) + } catch (err) { + console.error('Script exec error on contentReady', formItem.dataField, err) + } + }) + } + + runReadOnlyScripts() + }, onFieldDataChanged: (e) => { if (e.dataField) { const previousValue = editingFormDataRef.current?.[e.dataField] @@ -1396,15 +1590,25 @@ const Grid = (props: GridProps) => { } const formItem = gridDto.gridOptions.editingFormDto - .flatMap((group) => group.items || []) - .find((i) => i.dataField === e.dataField) + .flatMap((group) => flattenEditingFormItems([group])) + .find( + (i) => + String(i.dataField || '').toLowerCase() === + String(e.dataField || '').toLowerCase(), + ) if (formItem?.editorScript) { try { const grid = gridRef.current?.instance() const rowKey = grid?.option('editing.editRowKey') const rowIndex = rowKey !== undefined ? grid?.getRowIndexByKey(rowKey) : -1 - const formData = e.component?.option?.('formData') || {} + const formData = { + ...(e.component?.option?.('formData') || {}), + [e.dataField]: e.value, + } + editingFormDataRef.current = { ...formData } + const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) => + setFormEditorReadOnly(e.component, field, readOnly) const setFormData = (newData: any) => { e.component?.option?.('formData', newData) editingFormDataRef.current = { ...newData } @@ -1420,6 +1624,7 @@ const Grid = (props: GridProps) => { console.error('Script exec error', err) } } + } }, items: diff --git a/ui/src/views/list/Tree.tsx b/ui/src/views/list/Tree.tsx index f752c11..e42c31f 100644 --- a/ui/src/views/list/Tree.tsx +++ b/ui/src/views/list/Tree.tsx @@ -94,6 +94,138 @@ const getPersistedInsertedKey = (e: any, keyFieldName?: string) => { return undefined } +const updateReadOnlyInFormItems = (items: any[] = [], field: string, readOnly: boolean) => { + let changed = false + const expected = String(field || '').toLowerCase() + + const nextItems = items.map((item) => { + const key = item?.dataField || item?.name + let nextItem = item + + if (key && String(key).toLowerCase() === expected) { + const editorOptions = nextItem.editorOptions || {} + if (editorOptions.readOnly !== readOnly) { + changed = true + nextItem = { + ...nextItem, + editorOptions: { + ...editorOptions, + readOnly, + }, + } + } + } + + if (nextItem?.items?.length) { + const childResult = updateReadOnlyInFormItems(nextItem.items, field, readOnly) + if (childResult.changed) { + changed = true + nextItem = { ...nextItem, items: childResult.items } + } + } + + if (nextItem?.tabs?.length) { + const tabs = nextItem.tabs.map((tab: any) => { + const tabResult = updateReadOnlyInFormItems(tab.items, field, readOnly) + if (tabResult.changed) { + changed = true + return { ...tab, items: tabResult.items } + } + return tab + }) + nextItem = tabs === nextItem.tabs ? nextItem : { ...nextItem, tabs } + } + + return nextItem + }) + + return { items: nextItems, changed } +} + +const findFormFieldKey = (items: any[] = [], field: string): string => { + const expected = String(field || '').toLowerCase() + + for (const item of items || []) { + const key = item?.dataField || item?.name + if (key && String(key).toLowerCase() === expected) { + return key + } + + const childKey = findFormFieldKey(item?.items || [], field) + if (childKey) { + return childKey + } + + for (const tab of item?.tabs || []) { + const tabKey = findFormFieldKey(tab?.items || [], field) + if (tabKey) { + return tabKey + } + } + } + + return field +} + +const setFormEditorReadOnly = (form: any, field: string, readOnly: boolean) => { + if (!form?.option) return false + + const apply = () => { + const formItems = form.option('items') || [] + const resolvedField = findFormFieldKey(formItems, field) + const editor = form.getEditor?.(resolvedField) ?? form.getEditor?.(field) + const result = updateReadOnlyInFormItems(formItems, resolvedField, readOnly) + + if (result.changed) { + try { + const item = form.itemOption?.(resolvedField) ?? form.itemOption?.(field) + if (item) { + form.itemOption?.(resolvedField, 'editorOptions', { + ...(item.editorOptions || {}), + readOnly, + }) + } else { + form.option('items', result.items) + } + } catch { + form.option('items', result.items) + } + } + + const activeEditor = editor ?? form.getEditor?.(resolvedField) ?? form.getEditor?.(field) + if (activeEditor?.option?.('readOnly') !== readOnly) { + activeEditor?.option?.('readOnly', readOnly) + } + } + + apply() + return true +} + +const getActiveEditingForm = (component: any) => { + const editForm = component?.getView?.('editingView')?._editForm + return editForm?.getEditor || editForm?.itemOption ? editForm : undefined +} + +const flattenEditingFormItems = (items: any[] = []): EditingFormItemDto[] => + items.flatMap((item) => [ + ...(item?.dataField ? [item] : []), + ...flattenEditingFormItems(item?.items || []), + ...(item?.tabs || []).flatMap((tab: any) => flattenEditingFormItems(tab?.items || [])), + ]) + +const getValueByField = (data: Record = {}, field?: string) => { + if (!field) return undefined + if (Object.prototype.hasOwnProperty.call(data, field)) return data[field] + const key = Object.keys(data).find( + (itemKey) => itemKey.toLowerCase() === String(field).toLowerCase(), + ) + return key ? data[key] : undefined +} + +const shouldRunEditorScriptOnContentReady = (script?: string) => + Boolean(script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly'))) + const Tree = (props: TreeProps) => { const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props const { translate } = useLocalization() @@ -103,6 +235,7 @@ const Tree = (props: TreeProps) => { const refListFormCode = useRef('') const widgetGroupRef = useRef(null) const editingFormDataRef = useRef>({}) + const editingFormInstanceRef = useRef() // Edit popup state kaydetmeyi engellemek için flag const isEditingRef = useRef(false) @@ -520,7 +653,7 @@ const Tree = (props: TreeProps) => { function onEditorPreparing(editor: TreeListTypes.EditorPreparingEvent) { if (editor.parentType === 'dataRow' && editor.dataField && gridDto) { const formItem = gridDto.gridOptions.editingFormDto - .flatMap((group) => group.items || []) + .flatMap((group) => flattenEditingFormItems([group])) .find((i) => i.dataField === editor.dataField) const fieldName = editor.dataField.split(':')[0] const columnFormat = gridDto.columnFormats.find((column) => column.fieldName === fieldName) @@ -589,7 +722,7 @@ const Tree = (props: TreeProps) => { const editorInstance = formInstance.getEditor(col.fieldName!) if (editorInstance) { const formItem = gridDto.gridOptions.editingFormDto - .flatMap((group) => group.items || []) + .flatMap((group) => flattenEditingFormItems([group])) .find((i) => i.dataField === col.fieldName) const editorOptions = formItem?.editorOptions ? JSON.parse(formItem.editorOptions) @@ -622,6 +755,7 @@ const Tree = (props: TreeProps) => { if (formItem?.editorScript) { const prevHandler = editor.editorOptions.onValueChanged + const editorDataField = editor.dataField editor.editorOptions.onValueChanged = (e: any) => { if (prevHandler) prevHandler(e) @@ -631,7 +765,16 @@ const Tree = (props: TreeProps) => { const rowKey = grid.option('editing.editRowKey') const rowIndex = grid.getRowIndexByKey(rowKey) - const formData = grid.getVisibleRows().find((r) => r.key === rowKey)?.data || {} + const formData = { + ...(grid.getVisibleRows().find((r) => r.key === rowKey)?.data || {}), + [editorDataField]: e.value, + } + const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) => + setFormEditorReadOnly( + editingFormInstanceRef.current ?? getActiveEditingForm(grid), + field, + readOnly, + ) const setFormData = (newData: any) => { if (rowIndex >= 0) { @@ -1049,6 +1192,58 @@ const Tree = (props: TreeProps) => { }} form={{ colCount: 1, + onContentReady: (e) => { + editingFormInstanceRef.current = e.component + + const form = e.component + const grid = gridRef.current?.instance() + const rowKey = grid?.option('editing.editRowKey') + const rowIndex = rowKey !== undefined ? grid?.getRowIndexByKey(rowKey) : -1 + const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) => + setFormEditorReadOnly(form, field, readOnly) + const setFormData = (newData: any) => { + form?.option?.('formData', newData) + editingFormDataRef.current = { ...newData } + if (grid && rowIndex !== undefined && rowIndex >= 0) { + Object.keys(newData).forEach((field) => { + grid.cellValue(rowIndex, field, newData[field]) + }) + } + } + + const runReadOnlyScripts = () => { + const formData = { + ...editingFormDataRef.current, + ...(form?.option?.('formData') || {}), + } + editingFormDataRef.current = { ...formData } + + gridDto.gridOptions.editingFormDto + .flatMap((group) => flattenEditingFormItems([group])) + .filter((formItem) => + shouldRunEditorScriptOnContentReady(formItem.editorScript), + ) + .forEach((formItem) => { + try { + const editor = { + dataField: formItem.dataField, + component: grid, + } + const e = { + component: form, + dataField: formItem.dataField, + value: getValueByField(formData, formItem.dataField), + } + + eval(formItem.editorScript!) + } catch (err) { + console.error('Script exec error on contentReady', formItem.dataField, err) + } + }) + } + + runReadOnlyScripts() + }, onFieldDataChanged: (e) => { if (e.dataField) { const previousValue = editingFormDataRef.current?.[e.dataField] @@ -1057,15 +1252,25 @@ const Tree = (props: TreeProps) => { } const formItem = gridDto.gridOptions.editingFormDto - .flatMap((group) => group.items || []) - .find((i) => i.dataField === e.dataField) + .flatMap((group) => flattenEditingFormItems([group])) + .find( + (i) => + String(i.dataField || '').toLowerCase() === + String(e.dataField || '').toLowerCase(), + ) if (formItem?.editorScript) { try { const grid = gridRef.current?.instance() const rowKey = grid?.option('editing.editRowKey') const rowIndex = rowKey !== undefined ? grid?.getRowIndexByKey(rowKey) : -1 - const formData = e.component?.option?.('formData') || {} + const formData = { + ...(e.component?.option?.('formData') || {}), + [e.dataField]: e.value, + } + editingFormDataRef.current = { ...formData } + const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) => + setFormEditorReadOnly(e.component, field, readOnly) const setFormData = (newData: any) => { e.component?.option?.('formData', newData) editingFormDataRef.current = { ...newData } @@ -1081,6 +1286,7 @@ const Tree = (props: TreeProps) => { console.error('Script exec error', err) } } + } }, items: