Birden fazla Editor Script oluşturma

This commit is contained in:
Sedat Öztürk 2026-06-09 22:24:47 +03:00
parent acb06587c4
commit c1e7f8cd1d
4 changed files with 167 additions and 54 deletions

View file

@ -163,25 +163,7 @@ function fieldLabel(value: string, fallback: string) {
return value || fallback return value || fallback
} }
function buildScript({ type BuildScriptArgs = {
currentField,
copyMappings,
daysStartField,
daysEndField,
daysTargetField,
selectedItemAmountEnabled,
rowAmountEnabled,
amountQuantityField,
amountUnitPriceField,
amountUomField,
amountTotalField,
timeDiffEnabled,
timeStartField,
timeEndField,
timeTargetField,
conditionalActions,
serviceCall,
}: {
currentField?: string currentField?: string
copyMappings: CopyMapping[] copyMappings: CopyMapping[]
daysStartField: string daysStartField: string
@ -199,7 +181,28 @@ function buildScript({
timeTargetField: string timeTargetField: string
conditionalActions: ConditionalAction[] conditionalActions: ConditionalAction[]
serviceCall: string serviceCall: string
}) { }
function buildScript(args: BuildScriptArgs) {
const {
currentField,
copyMappings,
daysStartField,
daysEndField,
daysTargetField,
selectedItemAmountEnabled,
rowAmountEnabled,
amountQuantityField,
amountUnitPriceField,
amountUomField,
amountTotalField,
timeDiffEnabled,
timeStartField,
timeEndField,
timeTargetField,
conditionalActions,
serviceCall,
} = args
const activeCopyMappings = copyMappings.filter((mapping) => mapping.source && mapping.target) const activeCopyMappings = copyMappings.filter((mapping) => mapping.source && mapping.target)
const hasCopyMappings = activeCopyMappings.length > 0 const hasCopyMappings = activeCopyMappings.length > 0
const hasDateDifference = Boolean(daysStartField && daysEndField && daysTargetField) const hasDateDifference = Boolean(daysStartField && daysEndField && daysTargetField)
@ -228,6 +231,56 @@ function buildScript({
return '' return ''
} }
const hasNonConditionalSelection =
hasCopyMappings ||
hasDateDifference ||
hasSelectedItemAmount ||
hasRowAmount ||
hasTimeDifference ||
hasServiceCall
if (
hasConditionalActions &&
(activeConditionalActions.length > 1 || hasNonConditionalSelection)
) {
const scriptBlocks: string[] = []
if (hasNonConditionalSelection) {
scriptBlocks.push(
buildScript({
...args,
conditionalActions: [],
}),
)
}
activeConditionalActions.forEach((action) => {
scriptBlocks.push(
buildScript({
...args,
copyMappings: [],
daysStartField: '',
daysEndField: '',
daysTargetField: '',
selectedItemAmountEnabled: false,
rowAmountEnabled: false,
amountQuantityField: '',
amountUnitPriceField: '',
amountUomField: '',
amountTotalField: '',
timeDiffEnabled: false,
timeStartField: '',
timeEndField: '',
timeTargetField: '',
conditionalActions: [action],
serviceCall: '',
}),
)
})
return scriptBlocks.filter(Boolean).join('\n\n')
}
const needsSetFormData = const needsSetFormData =
hasCopyMappings || hasCopyMappings ||
hasDateDifference || hasDateDifference ||
@ -271,13 +324,14 @@ function buildScript({
const lines: string[] = ['(async () => {'] const lines: string[] = ['(async () => {']
if (needsSetFormData || hasConditionalActions) { if (needsSetFormData || hasConditionalActions || hasServiceCall) {
lines.push( lines.push(
" const currentField = (typeof editor !== 'undefined' && editor?.dataField) || e?.dataField || " + " const currentField = (typeof editor !== 'undefined' && editor?.dataField) || e?.dataField || " +
quote(currentField || '') + quote(currentField || '') +
';', ';',
) )
lines.push(' const next = { ...formData, [currentField]: e?.value };') lines.push(' const next = { ...formData, [currentField]: e?.value };')
lines.push(' const isEditorScriptContentReady = e?.scriptEvent === "contentReady";')
} }
if (needsRollbackCurrentValue) { if (needsRollbackCurrentValue) {
@ -288,7 +342,7 @@ function buildScript({
if (needsSelectedItem) { if (needsSelectedItem) {
lines.push( lines.push(
' const selectedItem = e?.component?.option ? e.component.option("selectedItem") : null;', ' const selectedItem = (() => { try { return e?.component?.option ? e.component.option("selectedItem") : null; } catch { return null; } })();',
) )
} }
@ -317,7 +371,9 @@ function buildScript({
? `getByPath(selectedItem, ${quote(mapping.source)})` ? `getByPath(selectedItem, ${quote(mapping.source)})`
: `selectedItem[${quote(mapping.source)}]` : `selectedItem[${quote(mapping.source)}]`
lines.push( lines.push(
` next[${quote(mapping.target)}] = selectedItem ? ${source} : next[${quote(mapping.target)}];`, ` if (!isEditorScriptContentReady) next[${quote(
mapping.target,
)}] = selectedItem ? ${source} : next[${quote(mapping.target)}];`,
) )
}) })
@ -328,31 +384,35 @@ function buildScript({
amountTotalField amountTotalField
) { ) {
lines.push(' {') lines.push(' {')
lines.push(' if (!isEditorScriptContentReady) {')
lines.push(' const p = selectedItem || {};') lines.push(' const p = selectedItem || {};')
lines.push( lines.push(
` const q = Math.round((parseFloat(p[${quote(amountQuantityField)}]) || 0) * 100);`, ` const q = Math.round((parseFloat(p[${quote(amountQuantityField)}]) || 0) * 100);`,
) )
lines.push( lines.push(
` const u = Math.round((parseFloat(p[${quote(amountUnitPriceField)}]) || 0) * 100);`, ` const u = Math.round((parseFloat(p[${quote(amountUnitPriceField)}]) || 0) * 100);`,
) )
lines.push(` next[${quote(amountQuantityField)}] = q / 100;`) lines.push(` next[${quote(amountQuantityField)}] = q / 100;`)
lines.push(` next[${quote(amountUnitPriceField)}] = u / 100;`) lines.push(` next[${quote(amountUnitPriceField)}] = u / 100;`)
if (amountUomField) { if (amountUomField) {
lines.push(` next[${quote(amountUomField)}] = p[${quote(amountUomField)}];`) lines.push(` next[${quote(amountUomField)}] = p[${quote(amountUomField)}];`)
} }
lines.push(` next[${quote(amountTotalField)}] = Math.round((q * u) / 100) / 100;`) lines.push(` next[${quote(amountTotalField)}] = Math.round((q * u) / 100) / 100;`)
lines.push(' }')
lines.push(' }') lines.push(' }')
} }
if (rowAmountEnabled && amountQuantityField && amountUnitPriceField && amountTotalField) { if (rowAmountEnabled && amountQuantityField && amountUnitPriceField && amountTotalField) {
lines.push(' {') lines.push(' {')
lines.push(' if (!isEditorScriptContentReady) {')
lines.push( lines.push(
` const q = Math.round((parseFloat(next[${quote(amountQuantityField)}]) || 0) * 100);`, ` const q = Math.round((parseFloat(next[${quote(amountQuantityField)}]) || 0) * 100);`,
) )
lines.push( lines.push(
` const u = Math.round((parseFloat(next[${quote(amountUnitPriceField)}]) || 0) * 100);`, ` const u = Math.round((parseFloat(next[${quote(amountUnitPriceField)}]) || 0) * 100);`,
) )
lines.push(` next[${quote(amountTotalField)}] = Math.round((q * u) / 100) / 100;`) lines.push(` next[${quote(amountTotalField)}] = Math.round((q * u) / 100) / 100;`)
lines.push(' }')
lines.push(' }') lines.push(' }')
} }
@ -363,7 +423,7 @@ function buildScript({
lines.push(` const startDate = parseDate(next[${quote(daysStartField)}]);`) lines.push(` const startDate = parseDate(next[${quote(daysStartField)}]);`)
lines.push(` const endDate = parseDate(next[${quote(daysEndField)}]);`) lines.push(` const endDate = parseDate(next[${quote(daysEndField)}]);`)
lines.push( lines.push(
` next[${quote( ` if (!isEditorScriptContentReady) next[${quote(
daysTargetField, daysTargetField,
)}] = startDate && endDate ? Math.max(0, Math.floor((Date.UTC(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()) - Date.UTC(startDate.getFullYear(), startDate.getMonth(), startDate.getDate())) / (24 * 60 * 60 * 1000)) + 1) : null;`, )}] = startDate && endDate ? Math.max(0, Math.floor((Date.UTC(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()) - Date.UTC(startDate.getFullYear(), startDate.getMonth(), startDate.getDate())) / (24 * 60 * 60 * 1000)) + 1) : null;`,
) )
@ -371,18 +431,20 @@ function buildScript({
if (timeDiffEnabled && timeStartField && timeEndField && timeTargetField) { if (timeDiffEnabled && timeStartField && timeEndField && timeTargetField) {
lines.push(' {') lines.push(' {')
lines.push(' if (!isEditorScriptContentReady) {')
lines.push( lines.push(
' const toDate = value => !value ? null : (value instanceof Date ? value : new Date(value));', ' const toDate = value => !value ? null : (value instanceof Date ? value : new Date(value));',
) )
lines.push(` const startTime = toDate(next[${quote(timeStartField)}]);`) lines.push(` const startTime = toDate(next[${quote(timeStartField)}]);`)
lines.push(` const endTime = toDate(next[${quote(timeEndField)}]);`) lines.push(` const endTime = toDate(next[${quote(timeEndField)}]);`)
lines.push(' let hours = null;') lines.push(' let hours = null;')
lines.push(' if (startTime && endTime) {') lines.push(' if (startTime && endTime) {')
lines.push(' hours = (endTime - startTime) / 36e5;') lines.push(' hours = (endTime - startTime) / 36e5;')
lines.push(' if (hours < 0) hours += 24;') lines.push(' if (hours < 0) hours += 24;')
lines.push(' hours = Math.round(hours * 10) / 10;') lines.push(' hours = Math.round(hours * 10) / 10;')
lines.push(' }')
lines.push(` next[${quote(timeTargetField)}] = hours;`)
lines.push(' }') lines.push(' }')
lines.push(` next[${quote(timeTargetField)}] = hours;`)
lines.push(' }') lines.push(' }')
} }
@ -398,7 +460,7 @@ function buildScript({
return return
} }
lines.push(` if (${condition}) {`) lines.push(` if (!isEditorScriptContentReady && (${condition})) {`)
if (action.actionType === 'setField' && action.targetField) { if (action.actionType === 'setField' && action.targetField) {
lines.push( lines.push(
@ -456,11 +518,13 @@ function buildScript({
}) })
if (needsSetFormData) { if (needsSetFormData) {
lines.push(' if (typeof setFormData === "function") setFormData(next);') lines.push(
' if (!isEditorScriptContentReady && typeof setFormData === "function") setFormData(next);',
)
} }
if (serviceCall.trim()) { if (serviceCall.trim()) {
lines.push(` ${serviceCall.trim().replace(/;?$/, ';')}`) lines.push(` if (!isEditorScriptContentReady) ${serviceCall.trim().replace(/;?$/, ';')}`)
} }
lines.push('})();') lines.push('})();')
@ -537,6 +601,25 @@ function EditorScriptBuilderDialog({
setTimeTargetField((current) => current || findField('TotalHours')) setTimeTargetField((current) => current || findField('TotalHours'))
} }
const resetBuilderSelections = () => {
setCopyMappings([])
setDaysStartField('')
setDaysEndField('')
setDaysTargetField('')
setSelectedItemAmountEnabled(false)
setRowAmountEnabled(false)
setAmountQuantityField('')
setAmountUnitPriceField('')
setAmountUomField('')
setAmountTotalField('')
setTimeDiffEnabled(false)
setTimeStartField('')
setTimeEndField('')
setTimeTargetField('')
setConditionalActions([])
setServiceCall('')
}
const hydrateKnownScript = (script?: string) => { const hydrateKnownScript = (script?: string) => {
const source = script?.trim() const source = script?.trim()
if (!source) return false if (!source) return false
@ -644,6 +727,21 @@ function EditorScriptBuilderDialog({
[generatedScript], [generatedScript],
) )
const canAppendGeneratedScript = Boolean(formattedGeneratedScript.trim())
const appendGeneratedScriptToEditor = () => {
const nextScriptBlock = formattedGeneratedScript.trim()
if (!nextScriptBlock) return
setScriptEditorValue((current) => {
const existingScript = current.trim()
if (existingScript === lastGeneratedScript.trim()) return nextScriptBlock
return existingScript ? `${existingScript}\n\n${nextScriptBlock}` : nextScriptBlock
})
setLastGeneratedScript('')
resetBuilderSelections()
}
useEffect(() => { useEffect(() => {
if (!isOpen) return if (!isOpen) return
setScriptEditorValue((current) => { setScriptEditorValue((current) => {
@ -757,7 +855,7 @@ function EditorScriptBuilderDialog({
</div> </div>
<div className="grid min-h-0 grid-cols-1 gap-4 overflow-y-auto pr-1 lg:grid-cols-2"> <div className="grid min-h-0 grid-cols-1 gap-4 overflow-y-auto pr-1 lg:grid-cols-2">
<section className="flex flex-col gap-4"> <section className="flex flex-col gap-2">
<div className="rounded border border-gray-200 dark:border-gray-700 p-3"> <div className="rounded border border-gray-200 dark:border-gray-700 p-3">
<div className="flex items-center justify-between gap-2 mb-3"> <div className="flex items-center justify-between gap-2 mb-3">
<div> <div>
@ -1212,6 +1310,17 @@ function EditorScriptBuilderDialog({
<span className="rounded bg-gray-100 px-2 py-1 text-[11px] font-medium text-gray-500 dark:bg-gray-800 dark:text-gray-300"> <span className="rounded bg-gray-100 px-2 py-1 text-[11px] font-medium text-gray-500 dark:bg-gray-800 dark:text-gray-300">
TypeScript TypeScript
</span> </span>
<Button
size="xs"
type="button"
variant="solid"
icon={<FaPlus />}
disabled={!canAppendGeneratedScript}
title="Soldaki builder seçimlerinin tamamını mevcut scriptin sonuna yeni bir blok olarak ekle."
onClick={appendGeneratedScriptToEditor}
>
Script olarak ekle
</Button>
<Button <Button
size="xs" size="xs"
type="button" type="button"
@ -1225,7 +1334,7 @@ function EditorScriptBuilderDialog({
</div> </div>
<div className="flex-1 min-h-[350px] overflow-hidden rounded border border-gray-200 dark:border-gray-700"> <div className="flex-1 min-h-[350px] overflow-hidden rounded border border-gray-200 dark:border-gray-700">
<Editor <Editor
height="100%" height="95%"
language="typescript" language="typescript"
theme="vs-dark" theme="vs-dark"
value={scriptEditorValue} value={scriptEditorValue}

View file

@ -253,6 +253,7 @@ const FormDevExpress = (props: {
const e = { const e = {
component: form, component: form,
dataField, dataField,
scriptEvent: 'valueChanged',
value: eventValue, value: eventValue,
} }
const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) => const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) =>
@ -414,6 +415,7 @@ const FormDevExpress = (props: {
const e = { const e = {
component: form, component: form,
dataField: formItem.dataField, dataField: formItem.dataField,
scriptEvent: 'contentReady',
value: getValueByField(currentFormData, formItem.dataField), value: getValueByField(currentFormData, formItem.dataField),
} }
const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) => const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) =>
@ -424,7 +426,7 @@ const FormDevExpress = (props: {
e, e,
editor, editor,
runtimeSetEditorReadOnly, runtimeSetEditorReadOnly,
setFormData: (newData: any) => applyEditorScriptFormData(form, newData), setFormData: undefined,
}) })
} catch (err) { } catch (err) {
console.error('Script execution failed on contentReady for', formItem.name, err) console.error('Script execution failed on contentReady for', formItem.name, err)

View file

@ -861,7 +861,7 @@ const Grid = (props: GridProps) => {
executeEditorScript(formItem.editorScript!, { executeEditorScript(formItem.editorScript!, {
formData, formData,
e, e: { ...e, scriptEvent: 'valueChanged' },
editor, editor,
runtimeSetEditorReadOnly, runtimeSetEditorReadOnly,
setFormData, setFormData,
@ -1665,6 +1665,7 @@ const Grid = (props: GridProps) => {
const e = { const e = {
component: form, component: form,
dataField: formItem.dataField, dataField: formItem.dataField,
scriptEvent: 'contentReady',
value: editorValue, value: editorValue,
} }
@ -1673,7 +1674,7 @@ const Grid = (props: GridProps) => {
e, e,
editor, editor,
runtimeSetEditorReadOnly, runtimeSetEditorReadOnly,
setFormData, setFormData: undefined,
}) })
} catch (err) { } catch (err) {
console.error( console.error(
@ -1726,7 +1727,7 @@ const Grid = (props: GridProps) => {
executeEditorScript(formItem.editorScript, { executeEditorScript(formItem.editorScript, {
formData, formData,
e, e: { ...e, scriptEvent: 'valueChanged' },
editor: { editor: {
dataField: e.dataField, dataField: e.dataField,
component: grid, component: grid,

View file

@ -819,7 +819,7 @@ const Tree = (props: TreeProps) => {
executeEditorScript(formItem.editorScript!, { executeEditorScript(formItem.editorScript!, {
formData, formData,
e, e: { ...e, scriptEvent: 'valueChanged' },
editor, editor,
runtimeSetEditorReadOnly, runtimeSetEditorReadOnly,
setFormData, setFormData,
@ -1326,6 +1326,7 @@ const Tree = (props: TreeProps) => {
const e = { const e = {
component: form, component: form,
dataField: formItem.dataField, dataField: formItem.dataField,
scriptEvent: 'contentReady',
value: editorValue, value: editorValue,
} }
@ -1334,7 +1335,7 @@ const Tree = (props: TreeProps) => {
e, e,
editor, editor,
runtimeSetEditorReadOnly, runtimeSetEditorReadOnly,
setFormData, setFormData: undefined,
}) })
} catch (err) { } catch (err) {
console.error( console.error(
@ -1387,7 +1388,7 @@ const Tree = (props: TreeProps) => {
executeEditorScript(formItem.editorScript, { executeEditorScript(formItem.editorScript, {
formData, formData,
e, e: { ...e, scriptEvent: 'valueChanged' },
editor: { editor: {
dataField: e.dataField, dataField: e.dataField,
component: grid, component: grid,