Helper Codes güncellemeleri ve EditorScript düzenlemesi
This commit is contained in:
parent
96f7091d46
commit
e1c808310d
6 changed files with 863 additions and 1231 deletions
|
|
@ -26,14 +26,11 @@ const boolOptions = [
|
||||||
{ key: 'readOnly', label: 'readOnly' },
|
{ key: 'readOnly', label: 'readOnly' },
|
||||||
{ key: 'disabled', label: 'disabled' },
|
{ key: 'disabled', label: 'disabled' },
|
||||||
{ key: 'searchEnabled', label: 'searchEnabled' },
|
{ key: 'searchEnabled', label: 'searchEnabled' },
|
||||||
{ key: 'showDataBeforeSearch', label: 'showDataBeforeSearch' },
|
|
||||||
{ key: 'acceptCustomValue', label: 'acceptCustomValue' },
|
{ key: 'acceptCustomValue', label: 'acceptCustomValue' },
|
||||||
{ key: 'showSpinButtons', label: 'showSpinButtons' },
|
{ key: 'showSpinButtons', label: 'showSpinButtons' },
|
||||||
{ key: 'useMaskBehavior', label: 'useMaskBehavior' },
|
{ key: 'useMaskBehavior', label: 'useMaskBehavior' },
|
||||||
{ key: 'useMaskedValue', label: 'useMaskedValue' },
|
{ key: 'useMaskedValue', label: 'useMaskedValue' },
|
||||||
{ key: 'spellcheck', label: 'spellcheck' },
|
{ key: 'spellcheck', label: 'spellcheck' },
|
||||||
{ key: 'openOnFieldClick', label: 'openOnFieldClick' },
|
|
||||||
{ key: 'showDropDownButton', label: 'showDropDownButton' },
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const htmlToolbarItems = [
|
const htmlToolbarItems = [
|
||||||
|
|
@ -302,10 +299,6 @@ function EditorOptionsBuilderDialog({
|
||||||
>
|
>
|
||||||
Editor Options Builder
|
Editor Options Builder
|
||||||
</h5>
|
</h5>
|
||||||
<FaSlidersH
|
|
||||||
className="text-gray-400"
|
|
||||||
title="Bu dialog editorOptions alanını metin yazmadan oluşturmak için kullanılır."
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{parseError && (
|
{parseError && (
|
||||||
|
|
@ -314,8 +307,8 @@ function EditorOptionsBuilderDialog({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="grid min-h-0 grid-cols-1 gap-4 overflow-y-auto pr-1 lg:grid-cols-3">
|
<div className="grid grid-cols-2 min-h-0 gap-4 overflow-y-auto pr-1 ">
|
||||||
<section className="lg:col-span-2 flex flex-col gap-4">
|
<section className="flex flex-col gap-4">
|
||||||
<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
|
<div
|
||||||
className="mb-3 text-sm font-semibold"
|
className="mb-3 text-sm font-semibold"
|
||||||
|
|
@ -327,7 +320,7 @@ function EditorOptionsBuilderDialog({
|
||||||
{boolOptions.map((option) => (
|
{boolOptions.map((option) => (
|
||||||
<label
|
<label
|
||||||
key={option.key}
|
key={option.key}
|
||||||
className="flex items-center gap-2 rounded border border-gray-100 dark:border-gray-700 px-2 py-2 text-sm"
|
className="flex items-center gap-2 rounded px-1 py-1 text-sm"
|
||||||
title={`${option.key}: true/false olarak editorOptions içine yazılır.`}
|
title={`${option.key}: true/false olarak editorOptions içine yazılır.`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
|
@ -565,7 +558,7 @@ function EditorOptionsBuilderDialog({
|
||||||
>
|
>
|
||||||
5. NumberBox
|
5. NumberBox
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
<div className="grid grid-cols-1 md:grid-cols-5 gap-3">
|
||||||
{['min', 'max', 'step'].map((key) => (
|
{['min', 'max', 'step'].map((key) => (
|
||||||
<label key={key} className="text-xs text-gray-500">
|
<label key={key} className="text-xs text-gray-500">
|
||||||
{key}
|
{key}
|
||||||
|
|
@ -585,7 +578,7 @@ function EditorOptionsBuilderDialog({
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
<label className="text-xs text-gray-500">
|
<label className="text-xs text-gray-500">
|
||||||
format.type
|
type
|
||||||
<select
|
<select
|
||||||
className={baseInputClass}
|
className={baseInputClass}
|
||||||
value={toSelectValue(getByPath(options, 'format.type'))}
|
value={toSelectValue(getByPath(options, 'format.type'))}
|
||||||
|
|
@ -600,7 +593,7 @@ function EditorOptionsBuilderDialog({
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<label className="text-xs text-gray-500">
|
<label className="text-xs text-gray-500">
|
||||||
format.precision
|
precision
|
||||||
<input
|
<input
|
||||||
className={baseInputClass}
|
className={baseInputClass}
|
||||||
value={toInputValue(getByPath(options, 'format.precision'))}
|
value={toInputValue(getByPath(options, 'format.precision'))}
|
||||||
|
|
@ -706,12 +699,106 @@ function EditorOptionsBuilderDialog({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<div
|
||||||
|
className="text-sm font-semibold"
|
||||||
|
title="DevExtreme dokümanındaki herhangi bir editorOptions path'ini eklemek için kullan. Örnek path: toolbar.multiline, maskRules.X, inputAttr.aria-label."
|
||||||
|
>
|
||||||
|
7. Özel Ayarlar
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
size="sm"
|
||||||
|
icon={<FaPlus />}
|
||||||
|
onClick={() =>
|
||||||
|
setCustomOptions((current) => [
|
||||||
|
...current,
|
||||||
|
{
|
||||||
|
id: `${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
||||||
|
path: '',
|
||||||
|
value: '',
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Ekle
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
{customOptions.map((option) => (
|
||||||
|
<div key={option.id} className="grid grid-cols-12 gap-2">
|
||||||
|
<input
|
||||||
|
className={`${baseInputClass} col-span-4`}
|
||||||
|
value={option.path}
|
||||||
|
onChange={(event) =>
|
||||||
|
setCustomOptions((current) =>
|
||||||
|
current.map((item) =>
|
||||||
|
item.id === option.id ? { ...item, path: event.target.value } : item,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
placeholder="path.to.option"
|
||||||
|
title="Nokta ile nested path yaz. Örnek: toolbar.multiline"
|
||||||
|
/>
|
||||||
|
<select
|
||||||
|
className={`${baseInputClass} col-span-2`}
|
||||||
|
value={option.type}
|
||||||
|
onChange={(event) =>
|
||||||
|
setCustomOptions((current) =>
|
||||||
|
current.map((item) =>
|
||||||
|
item.id === option.id
|
||||||
|
? { ...item, type: event.target.value as CustomOption['type'] }
|
||||||
|
: item,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<option value="string">string</option>
|
||||||
|
<option value="number">number</option>
|
||||||
|
<option value="boolean">boolean</option>
|
||||||
|
<option value="json">json</option>
|
||||||
|
</select>
|
||||||
|
<input
|
||||||
|
className={`${baseInputClass} col-span-5`}
|
||||||
|
value={option.value}
|
||||||
|
onChange={(event) =>
|
||||||
|
setCustomOptions((current) =>
|
||||||
|
current.map((item) =>
|
||||||
|
item.id === option.id ? { ...item, value: event.target.value } : item,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
placeholder={option.type === 'boolean' ? 'true / false' : 'value'}
|
||||||
|
title="Tip JSON ise object/array yazabilirsin. Tip boolean ise true veya false yaz."
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
shape="circle"
|
||||||
|
variant="plain"
|
||||||
|
icon={<FaTrash />}
|
||||||
|
onClick={() =>
|
||||||
|
setCustomOptions((current) =>
|
||||||
|
current.filter((item) => item.id !== option.id),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="rounded border border-gray-200 dark:border-gray-700 p-3 flex flex-col min-h-[420px]">
|
||||||
<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
|
<div
|
||||||
className="mb-3 text-sm font-semibold"
|
className="mb-3 text-sm font-semibold"
|
||||||
title="Daha önce kullandığın hazır editorOptions örnekleri. Tıkladığın preset mevcut JSON ile birleştirilir, yani başka seçenekleri silmeden üzerine ekler."
|
title="Daha önce kullandığın hazır editorOptions örnekleri. Tıkladığın preset mevcut JSON ile birleştirilir, yani başka seçenekleri silmeden üzerine ekler."
|
||||||
>
|
>
|
||||||
7. Hazır Ayarlar
|
Hazır Ayarlar
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -886,108 +973,14 @@ function EditorOptionsBuilderDialog({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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>
|
|
||||||
<div
|
<div
|
||||||
className="text-sm font-semibold"
|
className="flex items-center gap-2 text-sm font-semibold mt-3"
|
||||||
title="DevExtreme dokümanındaki herhangi bir editorOptions path'ini eklemek için kullan. Örnek path: toolbar.multiline, maskRules.X, inputAttr.aria-label."
|
|
||||||
>
|
|
||||||
8. Özel Ayarlar
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
size="sm"
|
|
||||||
icon={<FaPlus />}
|
|
||||||
onClick={() =>
|
|
||||||
setCustomOptions((current) => [
|
|
||||||
...current,
|
|
||||||
{
|
|
||||||
id: `${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
||||||
path: '',
|
|
||||||
value: '',
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Ekle
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
{customOptions.map((option) => (
|
|
||||||
<div key={option.id} className="grid grid-cols-12 gap-2">
|
|
||||||
<input
|
|
||||||
className={`${baseInputClass} col-span-4`}
|
|
||||||
value={option.path}
|
|
||||||
onChange={(event) =>
|
|
||||||
setCustomOptions((current) =>
|
|
||||||
current.map((item) =>
|
|
||||||
item.id === option.id ? { ...item, path: event.target.value } : item,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
placeholder="path.to.option"
|
|
||||||
title="Nokta ile nested path yaz. Örnek: toolbar.multiline"
|
|
||||||
/>
|
|
||||||
<select
|
|
||||||
className={`${baseInputClass} col-span-2`}
|
|
||||||
value={option.type}
|
|
||||||
onChange={(event) =>
|
|
||||||
setCustomOptions((current) =>
|
|
||||||
current.map((item) =>
|
|
||||||
item.id === option.id
|
|
||||||
? { ...item, type: event.target.value as CustomOption['type'] }
|
|
||||||
: item,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<option value="string">string</option>
|
|
||||||
<option value="number">number</option>
|
|
||||||
<option value="boolean">boolean</option>
|
|
||||||
<option value="json">json</option>
|
|
||||||
</select>
|
|
||||||
<input
|
|
||||||
className={`${baseInputClass} col-span-5`}
|
|
||||||
value={option.value}
|
|
||||||
onChange={(event) =>
|
|
||||||
setCustomOptions((current) =>
|
|
||||||
current.map((item) =>
|
|
||||||
item.id === option.id ? { ...item, value: event.target.value } : item,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
placeholder={option.type === 'boolean' ? 'true / false' : 'value'}
|
|
||||||
title="Tip JSON ise object/array yazabilirsin. Tip boolean ise true veya false yaz."
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
shape="circle"
|
|
||||||
variant="plain"
|
|
||||||
icon={<FaTrash />}
|
|
||||||
onClick={() =>
|
|
||||||
setCustomOptions((current) =>
|
|
||||||
current.filter((item) => item.id !== option.id),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="rounded border border-gray-200 dark:border-gray-700 p-3 flex flex-col min-h-[420px]">
|
|
||||||
<div
|
|
||||||
className="flex items-center gap-2 text-sm font-semibold mb-3"
|
|
||||||
title="Kaydet/Uygula sonrası editorOptions alanına yazılacak net JSON budur."
|
title="Kaydet/Uygula sonrası editorOptions alanına yazılacak net JSON budur."
|
||||||
>
|
>
|
||||||
<FaCode />
|
<FaCode />
|
||||||
JSON Önizleme
|
JSON Önizleme
|
||||||
</div>
|
</div>
|
||||||
<pre className="flex-1 overflow-auto rounded bg-gray-50 dark:bg-gray-900 p-3 text-xs whitespace-pre-wrap">
|
<pre className="flex overflow-auto rounded bg-gray-50 dark:bg-gray-900 p-3 text-xs whitespace-pre-wrap">
|
||||||
{preview || '{}'}
|
{preview || '{}'}
|
||||||
</pre>
|
</pre>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { Button, Dialog } from '@/components/ui'
|
import { Button, Dialog } from '@/components/ui'
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import Editor from '@monaco-editor/react'
|
||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import { FaBolt, FaCheck, FaCode, FaPlus, FaTimes, FaTrash } from 'react-icons/fa'
|
import { FaBolt, FaCheck, FaCode, FaPlus, FaTimes, FaTrash } from 'react-icons/fa'
|
||||||
|
|
||||||
|
|
@ -10,16 +10,6 @@ type CopyMapping = {
|
||||||
target: string
|
target: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ToggleRule = {
|
|
||||||
id: string
|
|
||||||
target: string
|
|
||||||
property: 'visible' | 'disabled' | 'readOnly'
|
|
||||||
source: string
|
|
||||||
operator: 'equals' | 'notEquals' | 'empty' | 'notEmpty'
|
|
||||||
value: string
|
|
||||||
whenTrue: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConditionalAction = {
|
type ConditionalAction = {
|
||||||
id: string
|
id: string
|
||||||
source: string
|
source: string
|
||||||
|
|
@ -35,18 +25,13 @@ type ConditionalAction = {
|
||||||
value: string
|
value: string
|
||||||
actionType:
|
actionType:
|
||||||
| 'setField'
|
| 'setField'
|
||||||
| 'copySelected'
|
|
||||||
| 'setFieldState'
|
|
||||||
| 'setFieldStyle'
|
|
||||||
| 'apiToField'
|
| 'apiToField'
|
||||||
| 'openUrl'
|
| 'openUrl'
|
||||||
| 'alert'
|
| 'alert'
|
||||||
| 'confirm'
|
| 'confirm'
|
||||||
| 'calculate'
|
| 'calculate'
|
||||||
targetField: string
|
targetField: string
|
||||||
targetProperty: 'visible' | 'disabled' | 'readOnly' | 'color' | 'backgroundColor' | 'borderColor'
|
|
||||||
textValue: string
|
textValue: string
|
||||||
selectedColumn: string
|
|
||||||
apiUrl: string
|
apiUrl: string
|
||||||
apiMethod: 'GET' | 'POST'
|
apiMethod: 'GET' | 'POST'
|
||||||
responsePath: string
|
responsePath: string
|
||||||
|
|
@ -76,9 +61,7 @@ const createConditionalAction = (): ConditionalAction => ({
|
||||||
value: '',
|
value: '',
|
||||||
actionType: 'setField',
|
actionType: 'setField',
|
||||||
targetField: '',
|
targetField: '',
|
||||||
targetProperty: 'disabled',
|
|
||||||
textValue: '',
|
textValue: '',
|
||||||
selectedColumn: '',
|
|
||||||
apiUrl: '',
|
apiUrl: '',
|
||||||
apiMethod: 'GET',
|
apiMethod: 'GET',
|
||||||
responsePath: '',
|
responsePath: '',
|
||||||
|
|
@ -98,14 +81,6 @@ function quote(value: string) {
|
||||||
return JSON.stringify(value ?? '')
|
return JSON.stringify(value ?? '')
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildCondition(rule: ToggleRule) {
|
|
||||||
const source = `next[${quote(rule.source)}]`
|
|
||||||
if (rule.operator === 'empty') return `!${source}`
|
|
||||||
if (rule.operator === 'notEmpty') return `!!${source}`
|
|
||||||
if (rule.operator === 'notEquals') return `${source} !== ${quote(rule.value)}`
|
|
||||||
return `${source} === ${quote(rule.value)}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildGenericCondition(rule: ConditionalAction) {
|
function buildGenericCondition(rule: ConditionalAction) {
|
||||||
if (rule.operator === 'always') return 'true'
|
if (rule.operator === 'always') return 'true'
|
||||||
const source = `next[${quote(rule.source)}]`
|
const source = `next[${quote(rule.source)}]`
|
||||||
|
|
@ -119,11 +94,11 @@ function buildGenericCondition(rule: ConditionalAction) {
|
||||||
return `${source} === ${quote(rule.value)}`
|
return `${source} === ${quote(rule.value)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function conditionNeedsSource(operator: ConditionalAction['operator'] | ToggleRule['operator']) {
|
function conditionNeedsSource(operator: ConditionalAction['operator']) {
|
||||||
return operator !== 'always'
|
return operator !== 'always'
|
||||||
}
|
}
|
||||||
|
|
||||||
function conditionNeedsValue(operator: ConditionalAction['operator'] | ToggleRule['operator']) {
|
function conditionNeedsValue(operator: ConditionalAction['operator']) {
|
||||||
return (
|
return (
|
||||||
operator === 'equals' ||
|
operator === 'equals' ||
|
||||||
operator === 'notEquals' ||
|
operator === 'notEquals' ||
|
||||||
|
|
@ -138,11 +113,6 @@ function isConditionalActionReady(action: ConditionalAction) {
|
||||||
if (conditionNeedsValue(action.operator) && !action.value.trim()) return false
|
if (conditionNeedsValue(action.operator) && !action.value.trim()) return false
|
||||||
if (action.actionType === 'setField')
|
if (action.actionType === 'setField')
|
||||||
return Boolean(action.targetField && action.textValue.trim())
|
return Boolean(action.targetField && action.textValue.trim())
|
||||||
if (action.actionType === 'copySelected')
|
|
||||||
return Boolean(action.targetField && action.selectedColumn)
|
|
||||||
if (action.actionType === 'setFieldState') return Boolean(action.targetField)
|
|
||||||
if (action.actionType === 'setFieldStyle')
|
|
||||||
return Boolean(action.targetField && action.textValue.trim())
|
|
||||||
if (action.actionType === 'apiToField') return Boolean(action.targetField && action.apiUrl.trim())
|
if (action.actionType === 'apiToField') return Boolean(action.targetField && action.apiUrl.trim())
|
||||||
if (action.actionType === 'openUrl') return Boolean(action.textValue.trim())
|
if (action.actionType === 'openUrl') return Boolean(action.textValue.trim())
|
||||||
if (action.actionType === 'alert' || action.actionType === 'confirm')
|
if (action.actionType === 'alert' || action.actionType === 'confirm')
|
||||||
|
|
@ -151,20 +121,6 @@ function isConditionalActionReady(action: ConditionalAction) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
function isToggleRuleReady(rule: ToggleRule) {
|
|
||||||
if (!rule.target || !rule.source) return false
|
|
||||||
if (conditionNeedsValue(rule.operator) && !rule.value.trim()) return false
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildValueExpression(value: string) {
|
|
||||||
const trimmed = value.trim()
|
|
||||||
if (!trimmed) return "''"
|
|
||||||
if (trimmed === 'true' || trimmed === 'false') return trimmed
|
|
||||||
if (trimmed.startsWith('=') && trimmed.length > 1) return trimmed.slice(1)
|
|
||||||
return quote(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function fieldLabel(value: string, fallback: string) {
|
function fieldLabel(value: string, fallback: string) {
|
||||||
return value || fallback
|
return value || fallback
|
||||||
}
|
}
|
||||||
|
|
@ -172,7 +128,6 @@ function fieldLabel(value: string, fallback: string) {
|
||||||
function buildScript({
|
function buildScript({
|
||||||
currentField,
|
currentField,
|
||||||
copyMappings,
|
copyMappings,
|
||||||
toggleRules,
|
|
||||||
daysStartField,
|
daysStartField,
|
||||||
daysEndField,
|
daysEndField,
|
||||||
daysTargetField,
|
daysTargetField,
|
||||||
|
|
@ -188,11 +143,9 @@ function buildScript({
|
||||||
timeTargetField,
|
timeTargetField,
|
||||||
conditionalActions,
|
conditionalActions,
|
||||||
serviceCall,
|
serviceCall,
|
||||||
customScript,
|
|
||||||
}: {
|
}: {
|
||||||
currentField?: string
|
currentField?: string
|
||||||
copyMappings: CopyMapping[]
|
copyMappings: CopyMapping[]
|
||||||
toggleRules: ToggleRule[]
|
|
||||||
daysStartField: string
|
daysStartField: string
|
||||||
daysEndField: string
|
daysEndField: string
|
||||||
daysTargetField: string
|
daysTargetField: string
|
||||||
|
|
@ -208,12 +161,9 @@ function buildScript({
|
||||||
timeTargetField: string
|
timeTargetField: string
|
||||||
conditionalActions: ConditionalAction[]
|
conditionalActions: ConditionalAction[]
|
||||||
serviceCall: string
|
serviceCall: string
|
||||||
customScript: string
|
|
||||||
}) {
|
}) {
|
||||||
const activeCopyMappings = copyMappings.filter((mapping) => mapping.source && mapping.target)
|
const activeCopyMappings = copyMappings.filter((mapping) => mapping.source && mapping.target)
|
||||||
const activeToggleRules = toggleRules.filter(isToggleRuleReady)
|
|
||||||
const hasCopyMappings = activeCopyMappings.length > 0
|
const hasCopyMappings = activeCopyMappings.length > 0
|
||||||
const hasToggleRules = activeToggleRules.length > 0
|
|
||||||
const hasDateDifference = Boolean(daysStartField && daysEndField && daysTargetField)
|
const hasDateDifference = Boolean(daysStartField && daysEndField && daysTargetField)
|
||||||
const hasSelectedItemAmount = Boolean(
|
const hasSelectedItemAmount = Boolean(
|
||||||
selectedItemAmountEnabled && amountQuantityField && amountUnitPriceField && amountTotalField,
|
selectedItemAmountEnabled && amountQuantityField && amountUnitPriceField && amountTotalField,
|
||||||
|
|
@ -229,7 +179,6 @@ function buildScript({
|
||||||
const hasServiceCall = Boolean(serviceCall.trim())
|
const hasServiceCall = Boolean(serviceCall.trim())
|
||||||
const hasBuilderSelection =
|
const hasBuilderSelection =
|
||||||
hasCopyMappings ||
|
hasCopyMappings ||
|
||||||
hasToggleRules ||
|
|
||||||
hasDateDifference ||
|
hasDateDifference ||
|
||||||
hasSelectedItemAmount ||
|
hasSelectedItemAmount ||
|
||||||
hasRowAmount ||
|
hasRowAmount ||
|
||||||
|
|
@ -238,7 +187,7 @@ function buildScript({
|
||||||
hasServiceCall
|
hasServiceCall
|
||||||
|
|
||||||
if (!hasBuilderSelection) {
|
if (!hasBuilderSelection) {
|
||||||
return customScript.trim()
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const needsSetFormData =
|
const needsSetFormData =
|
||||||
|
|
@ -248,17 +197,15 @@ function buildScript({
|
||||||
hasRowAmount ||
|
hasRowAmount ||
|
||||||
hasTimeDifference ||
|
hasTimeDifference ||
|
||||||
activeConditionalActions.some((action) =>
|
activeConditionalActions.some((action) =>
|
||||||
['setField', 'copySelected', 'apiToField', 'calculate'].includes(action.actionType),
|
['setField', 'apiToField', 'calculate'].includes(action.actionType),
|
||||||
)
|
)
|
||||||
const needsSelectedItem =
|
const needsSelectedItem =
|
||||||
hasCopyMappings ||
|
hasCopyMappings ||
|
||||||
hasSelectedItemAmount ||
|
hasSelectedItemAmount ||
|
||||||
activeConditionalActions.some((action) =>
|
activeConditionalActions.some((action) =>
|
||||||
[
|
[
|
||||||
'copySelected',
|
|
||||||
'calculate',
|
'calculate',
|
||||||
'setField',
|
'setField',
|
||||||
'setFieldStyle',
|
|
||||||
'apiToField',
|
'apiToField',
|
||||||
'openUrl',
|
'openUrl',
|
||||||
'alert',
|
'alert',
|
||||||
|
|
@ -269,30 +216,21 @@ function buildScript({
|
||||||
activeCopyMappings.some((mapping) => mapping.source.includes('.')) ||
|
activeCopyMappings.some((mapping) => mapping.source.includes('.')) ||
|
||||||
activeConditionalActions.some(
|
activeConditionalActions.some(
|
||||||
(action) =>
|
(action) =>
|
||||||
action.actionType === 'copySelected' ||
|
|
||||||
action.actionType === 'apiToField' ||
|
action.actionType === 'apiToField' ||
|
||||||
['setField', 'setFieldStyle', 'openUrl', 'alert', 'confirm'].includes(action.actionType),
|
['setField', 'openUrl', 'alert', 'confirm'].includes(action.actionType),
|
||||||
)
|
)
|
||||||
const needsRenderTemplate = activeConditionalActions.some((action) =>
|
const needsRenderTemplate = activeConditionalActions.some((action) =>
|
||||||
['setField', 'setFieldStyle', 'apiToField', 'openUrl', 'alert', 'confirm'].includes(
|
['setField', 'apiToField', 'openUrl', 'alert', 'confirm'].includes(
|
||||||
action.actionType,
|
action.actionType,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
const needsForm =
|
const needsRollbackCurrentValue = activeConditionalActions.some(
|
||||||
hasToggleRules ||
|
(action) => action.actionType === 'confirm',
|
||||||
activeConditionalActions.some(
|
|
||||||
(action) => action.actionType === 'setFieldState' || action.actionType === 'setFieldStyle',
|
|
||||||
)
|
|
||||||
const needsFieldState =
|
|
||||||
hasToggleRules ||
|
|
||||||
activeConditionalActions.some((action) => action.actionType === 'setFieldState')
|
|
||||||
const needsFieldStyle = activeConditionalActions.some(
|
|
||||||
(action) => action.actionType === 'setFieldStyle',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const lines: string[] = ['(async () => {']
|
const lines: string[] = ['(async () => {']
|
||||||
|
|
||||||
if (needsSetFormData || hasToggleRules || hasConditionalActions) {
|
if (needsSetFormData || hasConditionalActions) {
|
||||||
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 || '') +
|
||||||
|
|
@ -301,6 +239,12 @@ function buildScript({
|
||||||
lines.push(' const next = { ...formData, [currentField]: e?.value };')
|
lines.push(' const next = { ...formData, [currentField]: e?.value };')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (needsRollbackCurrentValue) {
|
||||||
|
lines.push(
|
||||||
|
' const rollbackCurrentValue = () => { const previousFieldValue = typeof previousValue !== "undefined" ? previousValue : (e?.previousValue ?? formData?.[currentField] ?? null); next[currentField] = previousFieldValue; const currentEditor = e?.component?.getEditor?.(currentField); currentEditor?.option?.("value", previousFieldValue); if (!currentEditor) e?.component?.option?.("value", previousFieldValue); if (typeof setFormData === "function") setFormData({ ...next, [currentField]: previousFieldValue }); };',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (needsSelectedItem) {
|
if (needsSelectedItem) {
|
||||||
lines.push(
|
lines.push(
|
||||||
' const selectedItem = e?.component?.option ? e.component.option("selectedItem") : null;',
|
' const selectedItem = e?.component?.option ? e.component.option("selectedItem") : null;',
|
||||||
|
|
@ -319,24 +263,6 @@ function buildScript({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsForm) {
|
|
||||||
lines.push(
|
|
||||||
' const form = e?.component?.itemOption ? e.component : ((typeof editor !== "undefined" && editor?.component?.option) ? editor.component.option("editing.form") : null);',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needsFieldState) {
|
|
||||||
lines.push(
|
|
||||||
' const setFieldState = (field, prop, flag) => { if (!field) return; if (prop === "visible") { form?.itemOption?.(field, "visible", flag); return; } form?.getEditor?.(field)?.option(prop, flag); const item = form?.itemOption?.(field) || {}; form?.itemOption?.(field, "editorOptions", { ...(item.editorOptions || {}), [prop]: flag }); };',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needsFieldStyle) {
|
|
||||||
lines.push(
|
|
||||||
' const setFieldStyle = (field, prop, value) => { const editorInstance = form?.getEditor?.(field); const element = editorInstance?.element?.(); const node = element?.get ? element.get(0) : element; if (node?.style) node.style[prop] = value || ""; const input = node?.querySelector?.("input, textarea, .dx-texteditor-input"); if (input?.style) input.style[prop] = value || ""; };',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
activeCopyMappings.forEach((mapping) => {
|
activeCopyMappings.forEach((mapping) => {
|
||||||
const source = mapping.source.includes('.')
|
const source = mapping.source.includes('.')
|
||||||
? `getByPath(selectedItem, ${quote(mapping.source)})`
|
? `getByPath(selectedItem, ${quote(mapping.source)})`
|
||||||
|
|
@ -421,30 +347,6 @@ function buildScript({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.actionType === 'copySelected' && action.targetField && action.selectedColumn) {
|
|
||||||
lines.push(
|
|
||||||
` next[${quote(action.targetField)}] = selectedItem ? getByPath(selectedItem, ${quote(
|
|
||||||
action.selectedColumn,
|
|
||||||
)}) : next[${quote(action.targetField)}];`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.actionType === 'setFieldState' && action.targetField) {
|
|
||||||
lines.push(
|
|
||||||
` setFieldState(${quote(action.targetField)}, ${quote(action.targetProperty)}, ${buildValueExpression(
|
|
||||||
action.textValue || 'true',
|
|
||||||
)});`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.actionType === 'setFieldStyle' && action.targetField && action.textValue) {
|
|
||||||
lines.push(
|
|
||||||
` setFieldStyle(${quote(action.targetField)}, ${quote(
|
|
||||||
action.targetProperty,
|
|
||||||
)}, renderTemplate(${quote(action.textValue)}));`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.actionType === 'apiToField' && action.apiUrl && action.targetField) {
|
if (action.actionType === 'apiToField' && action.apiUrl && action.targetField) {
|
||||||
const method = action.apiMethod || 'GET'
|
const method = action.apiMethod || 'GET'
|
||||||
lines.push(` const apiUrl = renderTemplate(${quote(action.apiUrl)});`)
|
lines.push(` const apiUrl = renderTemplate(${quote(action.apiUrl)});`)
|
||||||
|
|
@ -476,7 +378,11 @@ function buildScript({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.actionType === 'confirm' && action.message) {
|
if (action.actionType === 'confirm' && action.message) {
|
||||||
lines.push(` if (!confirm(renderTemplate(${quote(action.message)}))) return;`)
|
lines.push(
|
||||||
|
` if (!confirm(renderTemplate(${quote(
|
||||||
|
action.message,
|
||||||
|
)}))) { rollbackCurrentValue(); return; }`,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.actionType === 'calculate' && action.targetField && action.formula) {
|
if (action.actionType === 'calculate' && action.targetField && action.formula) {
|
||||||
|
|
@ -494,33 +400,10 @@ function buildScript({
|
||||||
lines.push(' setFormData(next);')
|
lines.push(' setFormData(next);')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasToggleRules) {
|
|
||||||
activeToggleRules.forEach((rule) => {
|
|
||||||
const condition = buildCondition(rule)
|
|
||||||
lines.push(` {`)
|
|
||||||
lines.push(` const flag = (${condition}) ? ${rule.whenTrue} : ${!rule.whenTrue};`)
|
|
||||||
if (rule.property === 'visible') {
|
|
||||||
lines.push(` setFieldState(${quote(rule.target)}, 'visible', flag);`)
|
|
||||||
} else {
|
|
||||||
lines.push(` setFieldState(${quote(rule.target)}, ${quote(rule.property)}, flag);`)
|
|
||||||
}
|
|
||||||
lines.push(` }`)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serviceCall.trim()) {
|
if (serviceCall.trim()) {
|
||||||
lines.push(` ${serviceCall.trim().replace(/;?$/, ';')}`)
|
lines.push(` ${serviceCall.trim().replace(/;?$/, ';')}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (customScript.trim()) {
|
|
||||||
lines.push(' // Custom script')
|
|
||||||
customScript
|
|
||||||
.split('\n')
|
|
||||||
.map((line) => line.trimEnd())
|
|
||||||
.filter(Boolean)
|
|
||||||
.forEach((line) => lines.push(` ${line}`))
|
|
||||||
}
|
|
||||||
|
|
||||||
lines.push('})();')
|
lines.push('})();')
|
||||||
return lines.join('\n')
|
return lines.join('\n')
|
||||||
}
|
}
|
||||||
|
|
@ -535,7 +418,6 @@ function EditorScriptBuilderDialog({
|
||||||
}: EditorScriptBuilderDialogProps) {
|
}: EditorScriptBuilderDialogProps) {
|
||||||
const availableFields = useMemo(() => fieldOptions(fields), [fields])
|
const availableFields = useMemo(() => fieldOptions(fields), [fields])
|
||||||
const [copyMappings, setCopyMappings] = useState<CopyMapping[]>([])
|
const [copyMappings, setCopyMappings] = useState<CopyMapping[]>([])
|
||||||
const [toggleRules, setToggleRules] = useState<ToggleRule[]>([])
|
|
||||||
const [daysStartField, setDaysStartField] = useState('')
|
const [daysStartField, setDaysStartField] = useState('')
|
||||||
const [daysEndField, setDaysEndField] = useState('')
|
const [daysEndField, setDaysEndField] = useState('')
|
||||||
const [daysTargetField, setDaysTargetField] = useState('')
|
const [daysTargetField, setDaysTargetField] = useState('')
|
||||||
|
|
@ -551,13 +433,12 @@ function EditorScriptBuilderDialog({
|
||||||
const [timeTargetField, setTimeTargetField] = useState('')
|
const [timeTargetField, setTimeTargetField] = useState('')
|
||||||
const [conditionalActions, setConditionalActions] = useState<ConditionalAction[]>([])
|
const [conditionalActions, setConditionalActions] = useState<ConditionalAction[]>([])
|
||||||
const [serviceCall, setServiceCall] = useState('')
|
const [serviceCall, setServiceCall] = useState('')
|
||||||
const [customScript, setCustomScript] = useState('')
|
const [scriptEditorValue, setScriptEditorValue] = useState('')
|
||||||
const { translate } = useLocalization()
|
const [lastGeneratedScript, setLastGeneratedScript] = useState('')
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isOpen) return
|
if (!isOpen) return
|
||||||
setCopyMappings([])
|
setCopyMappings([])
|
||||||
setToggleRules([])
|
|
||||||
setDaysStartField('')
|
setDaysStartField('')
|
||||||
setDaysEndField('')
|
setDaysEndField('')
|
||||||
setDaysTargetField('')
|
setDaysTargetField('')
|
||||||
|
|
@ -573,7 +454,8 @@ function EditorScriptBuilderDialog({
|
||||||
setTimeTargetField('')
|
setTimeTargetField('')
|
||||||
setConditionalActions([])
|
setConditionalActions([])
|
||||||
setServiceCall('')
|
setServiceCall('')
|
||||||
setCustomScript('')
|
setScriptEditorValue(value?.trim() || '')
|
||||||
|
setLastGeneratedScript('')
|
||||||
}, [isOpen, value])
|
}, [isOpen, value])
|
||||||
|
|
||||||
const findField = (fieldName: string) =>
|
const findField = (fieldName: string) =>
|
||||||
|
|
@ -592,12 +474,6 @@ function EditorScriptBuilderDialog({
|
||||||
setTimeTargetField((current) => current || findField('TotalHours'))
|
setTimeTargetField((current) => current || findField('TotalHours'))
|
||||||
}
|
}
|
||||||
|
|
||||||
const fillDateDefaults = () => {
|
|
||||||
setDaysStartField((current) => current || findField('StartDate'))
|
|
||||||
setDaysEndField((current) => current || findField('EndDate'))
|
|
||||||
setDaysTargetField((current) => current || findField('TotalDays'))
|
|
||||||
}
|
|
||||||
|
|
||||||
const hydrateKnownScript = (script?: string) => {
|
const hydrateKnownScript = (script?: string) => {
|
||||||
const source = script?.trim()
|
const source = script?.trim()
|
||||||
if (!source) return false
|
if (!source) return false
|
||||||
|
|
@ -649,21 +525,20 @@ function EditorScriptBuilderDialog({
|
||||||
if (!isOpen) return
|
if (!isOpen) return
|
||||||
const existingScript = value?.trim()
|
const existingScript = value?.trim()
|
||||||
if (!existingScript) {
|
if (!existingScript) {
|
||||||
setCustomScript('')
|
setScriptEditorValue('')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const hydrated = hydrateKnownScript(existingScript)
|
const hydrated = hydrateKnownScript(existingScript)
|
||||||
setCustomScript(hydrated ? '' : existingScript)
|
setScriptEditorValue(hydrated ? '' : existingScript)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [availableFields, isOpen, value])
|
}, [availableFields, isOpen, value])
|
||||||
|
|
||||||
const preview = useMemo(
|
const generatedScript = useMemo(
|
||||||
() =>
|
() =>
|
||||||
buildScript({
|
buildScript({
|
||||||
currentField,
|
currentField,
|
||||||
copyMappings,
|
copyMappings,
|
||||||
toggleRules,
|
|
||||||
daysStartField,
|
daysStartField,
|
||||||
daysEndField,
|
daysEndField,
|
||||||
daysTargetField,
|
daysTargetField,
|
||||||
|
|
@ -679,7 +554,6 @@ function EditorScriptBuilderDialog({
|
||||||
timeTargetField,
|
timeTargetField,
|
||||||
conditionalActions,
|
conditionalActions,
|
||||||
serviceCall,
|
serviceCall,
|
||||||
customScript,
|
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
amountQuantityField,
|
amountQuantityField,
|
||||||
|
|
@ -689,7 +563,6 @@ function EditorScriptBuilderDialog({
|
||||||
conditionalActions,
|
conditionalActions,
|
||||||
copyMappings,
|
copyMappings,
|
||||||
currentField,
|
currentField,
|
||||||
customScript,
|
|
||||||
daysEndField,
|
daysEndField,
|
||||||
daysStartField,
|
daysStartField,
|
||||||
daysTargetField,
|
daysTargetField,
|
||||||
|
|
@ -700,10 +573,18 @@ function EditorScriptBuilderDialog({
|
||||||
timeEndField,
|
timeEndField,
|
||||||
timeStartField,
|
timeStartField,
|
||||||
timeTargetField,
|
timeTargetField,
|
||||||
toggleRules,
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) return
|
||||||
|
setScriptEditorValue((current) => {
|
||||||
|
if (current === lastGeneratedScript || !current.trim()) return generatedScript
|
||||||
|
return current
|
||||||
|
})
|
||||||
|
setLastGeneratedScript(generatedScript)
|
||||||
|
}, [generatedScript, isOpen, lastGeneratedScript])
|
||||||
|
|
||||||
const renderFieldSelect = (value: string, onChange: (value: string) => void) => (
|
const renderFieldSelect = (value: string, onChange: (value: string) => void) => (
|
||||||
<select
|
<select
|
||||||
className={baseInputClass}
|
className={baseInputClass}
|
||||||
|
|
@ -737,7 +618,7 @@ function EditorScriptBuilderDialog({
|
||||||
</label>
|
</label>
|
||||||
)
|
)
|
||||||
|
|
||||||
const needsCompareValue = (operator: ConditionalAction['operator'] | ToggleRule['operator']) =>
|
const needsCompareValue = (operator: ConditionalAction['operator']) =>
|
||||||
operator === 'equals' ||
|
operator === 'equals' ||
|
||||||
operator === 'notEquals' ||
|
operator === 'notEquals' ||
|
||||||
operator === 'contains' ||
|
operator === 'contains' ||
|
||||||
|
|
@ -762,19 +643,6 @@ function EditorScriptBuilderDialog({
|
||||||
if (action.actionType === 'setField') {
|
if (action.actionType === 'setField') {
|
||||||
return `${condition}, ${target} alanına ${action.textValue || 'değer'} yaz.`
|
return `${condition}, ${target} alanına ${action.textValue || 'değer'} yaz.`
|
||||||
}
|
}
|
||||||
if (action.actionType === 'copySelected') {
|
|
||||||
return `${condition}, seçili kayıttaki ${action.selectedColumn || 'path'} değerini ${target} alanına yaz.`
|
|
||||||
}
|
|
||||||
if (action.actionType === 'setFieldState') {
|
|
||||||
return `${condition}, ${target} alanında ${action.targetProperty} = ${
|
|
||||||
action.textValue || 'true'
|
|
||||||
} yap.`
|
|
||||||
}
|
|
||||||
if (action.actionType === 'setFieldStyle') {
|
|
||||||
return `${condition}, ${target} alanının ${action.targetProperty} stilini ${
|
|
||||||
action.textValue || 'değer'
|
|
||||||
} yap.`
|
|
||||||
}
|
|
||||||
if (action.actionType === 'apiToField') {
|
if (action.actionType === 'apiToField') {
|
||||||
return `${condition}, ${action.apiMethod} ${action.apiUrl || 'api url'} çağır ve sonucu ${target} alanına yaz.`
|
return `${condition}, ${action.apiMethod} ${action.apiUrl || 'api url'} çağır ve sonucu ${target} alanına yaz.`
|
||||||
}
|
}
|
||||||
|
|
@ -809,16 +677,12 @@ function EditorScriptBuilderDialog({
|
||||||
<h5
|
<h5
|
||||||
title={`${currentField || 'Geçerli alan'} değiştiğinde çalışacak editorScript üretir. Basit kopyalama ve hazır hesaplamalar için ilk bölümleri, API/URL/alert/özel koşul gibi esnek davranışlar için Koşullu Aksiyon bölümünü kullan.${
|
title={`${currentField || 'Geçerli alan'} değiştiğinde çalışacak editorScript üretir. Basit kopyalama ve hazır hesaplamalar için ilk bölümleri, API/URL/alert/özel koşul gibi esnek davranışlar için Koşullu Aksiyon bölümünü kullan.${
|
||||||
value?.trim()
|
value?.trim()
|
||||||
? ' Mevcut editorScript otomatik olarak özel script alanında korunur. Yeni bir builder seçeneği eklemezsen Uygula aynı scripti geri yazar.'
|
? ' Mevcut editorScript sağdaki TypeScript editor içinde korunur. Yeni bir builder seçeneği eklemezsen Uygula aynı scripti geri yazar.'
|
||||||
: ''
|
: ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
Editor Script Builder
|
Editor Script Builder
|
||||||
</h5>
|
</h5>
|
||||||
<FaBolt
|
|
||||||
className="text-gray-400"
|
|
||||||
title="Bu dialog editorScript alanını seçimlerle oluşturmak için kullanılır."
|
|
||||||
/>
|
|
||||||
</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">
|
||||||
|
|
@ -1005,24 +869,18 @@ function EditorScriptBuilderDialog({
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<option value="setField">Field değerini yaz</option>
|
<option value="setField">Field değerini yaz</option>
|
||||||
<option value="copySelected">Seçili kayıttan kopyala</option>
|
|
||||||
<option value="setFieldState">Field durumunu değiştir</option>
|
|
||||||
<option value="setFieldStyle">Field stilini değiştir</option>
|
|
||||||
<option value="apiToField">API sonucunu field'a yaz</option>
|
|
||||||
<option value="openUrl">URL aç</option>
|
<option value="openUrl">URL aç</option>
|
||||||
<option value="alert">Uyarı göster</option>
|
<option value="alert">Uyarı göster</option>
|
||||||
<option value="confirm">Onay iste</option>
|
<option value="confirm">Onay iste</option>
|
||||||
<option value="calculate">Formül hesapla</option>
|
<option value="calculate">Formül hesapla</option>
|
||||||
|
<option value="apiToField">API sonucunu field'a yaz</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
{[
|
{[
|
||||||
'setField',
|
'setField',
|
||||||
'copySelected',
|
|
||||||
'apiToField',
|
'apiToField',
|
||||||
'calculate',
|
'calculate',
|
||||||
'setFieldState',
|
|
||||||
'setFieldStyle',
|
|
||||||
].includes(action.actionType) && (
|
].includes(action.actionType) && (
|
||||||
<div className="col-span-12 md:col-span-4">
|
<div className="col-span-12 md:col-span-4">
|
||||||
{renderLabeledFieldSelect('Hedef field', action.targetField, (value) =>
|
{renderLabeledFieldSelect('Hedef field', action.targetField, (value) =>
|
||||||
|
|
@ -1043,80 +901,6 @@ function EditorScriptBuilderDialog({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{action.actionType === 'copySelected' && (
|
|
||||||
<input
|
|
||||||
className={`${baseInputClass} col-span-4`}
|
|
||||||
value={action.selectedColumn}
|
|
||||||
onChange={(event) =>
|
|
||||||
updateConditionalAction(action.id, {
|
|
||||||
selectedColumn: event.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
placeholder="selected item path"
|
|
||||||
title="Seçilen lookup kaydından okunacak path. Örnek: UomId veya Customer.Name"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{action.actionType === 'setFieldState' && (
|
|
||||||
<>
|
|
||||||
<select
|
|
||||||
className={`${baseInputClass} col-span-2`}
|
|
||||||
value={action.targetProperty}
|
|
||||||
title="Hedef field'ın değiştirilecek özelliği."
|
|
||||||
onChange={(event) =>
|
|
||||||
updateConditionalAction(action.id, {
|
|
||||||
targetProperty: event.target
|
|
||||||
.value as ConditionalAction['targetProperty'],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<option value="disabled">disabled</option>
|
|
||||||
<option value="readOnly">readOnly</option>
|
|
||||||
<option value="visible">visible</option>
|
|
||||||
</select>
|
|
||||||
<select
|
|
||||||
className={`${baseInputClass} col-span-2`}
|
|
||||||
value={action.textValue || 'true'}
|
|
||||||
title="Koşul sağlanınca property true mu false mu olsun?"
|
|
||||||
onChange={(event) =>
|
|
||||||
updateConditionalAction(action.id, { textValue: event.target.value })
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<option value="true">true</option>
|
|
||||||
<option value="false">false</option>
|
|
||||||
</select>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{action.actionType === 'setFieldStyle' && (
|
|
||||||
<>
|
|
||||||
<select
|
|
||||||
className={`${baseInputClass} col-span-2`}
|
|
||||||
value={action.targetProperty}
|
|
||||||
title="Koşul sağlandığında hedef field üzerinde uygulanacak CSS style property'si. Örnek: color, backgroundColor, borderColor."
|
|
||||||
onChange={(event) =>
|
|
||||||
updateConditionalAction(action.id, {
|
|
||||||
targetProperty: event.target
|
|
||||||
.value as ConditionalAction['targetProperty'],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<option value="color">color</option>
|
|
||||||
<option value="backgroundColor">backgroundColor</option>
|
|
||||||
<option value="borderColor">borderColor</option>
|
|
||||||
</select>
|
|
||||||
<input
|
|
||||||
className={`${baseInputClass} col-span-2`}
|
|
||||||
value={action.textValue}
|
|
||||||
onChange={(event) =>
|
|
||||||
updateConditionalAction(action.id, { textValue: event.target.value })
|
|
||||||
}
|
|
||||||
placeholder="#ef4444"
|
|
||||||
title="CSS renk değeri yaz. Örnek: red, #ef4444, rgb(239,68,68). Token da kullanabilirsin: {ColorCode}"
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{action.actionType === 'apiToField' && (
|
{action.actionType === 'apiToField' && (
|
||||||
<>
|
<>
|
||||||
<select
|
<select
|
||||||
|
|
@ -1212,222 +996,19 @@ function EditorScriptBuilderDialog({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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>
|
|
||||||
<div
|
<div
|
||||||
className="text-sm font-semibold"
|
className="mb-2 text-sm font-semibold"
|
||||||
title="Bir kaynak alan belirli bir koşulu sağladığında hedef alanı disabled/readOnly/visible yapar. Örnek: PhoneNumber 1000 ise Surname disabled true."
|
|
||||||
>
|
|
||||||
3. Field Durum Kuralları
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
size="sm"
|
|
||||||
icon={<FaPlus />}
|
|
||||||
title="Basit field state kuralı ekler. Örnek: kaynak alan boşsa hedef alan disabled true olsun."
|
|
||||||
onClick={() =>
|
|
||||||
setToggleRules((current) => [
|
|
||||||
...current,
|
|
||||||
{
|
|
||||||
id: makeId(),
|
|
||||||
target: '',
|
|
||||||
property: 'disabled',
|
|
||||||
source: currentField || '',
|
|
||||||
operator: 'empty',
|
|
||||||
value: '',
|
|
||||||
whenTrue: true,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Ekle
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
{toggleRules.map((rule) => (
|
|
||||||
<div
|
|
||||||
key={rule.id}
|
|
||||||
className="rounded border border-gray-100 dark:border-gray-700 p-3"
|
|
||||||
>
|
|
||||||
<div className="mb-3 rounded bg-gray-50 px-2 py-1.5 text-xs text-gray-600 dark:bg-gray-900 dark:text-gray-300">
|
|
||||||
Eğer{' '}
|
|
||||||
<span className="font-semibold">
|
|
||||||
{fieldLabel(rule.source, 'kaynak field')}
|
|
||||||
</span>{' '}
|
|
||||||
<span className="font-semibold">{rule.operator}</span>{' '}
|
|
||||||
{rule.operator === 'equals' || rule.operator === 'notEquals' ? (
|
|
||||||
<span className="font-semibold">{rule.value || 'değer'}</span>
|
|
||||||
) : null}{' '}
|
|
||||||
ise{' '}
|
|
||||||
<span className="font-semibold">
|
|
||||||
{fieldLabel(rule.target, 'hedef field')}
|
|
||||||
</span>{' '}
|
|
||||||
<span className="font-semibold">{rule.property}</span> ={' '}
|
|
||||||
<span className="font-semibold">{String(rule.whenTrue)}</span> olur.
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-12 gap-2">
|
|
||||||
<div className="col-span-12 md:col-span-4">
|
|
||||||
{renderLabeledFieldSelect(
|
|
||||||
'O zaman: hedef field',
|
|
||||||
rule.target,
|
|
||||||
(nextValue) =>
|
|
||||||
setToggleRules((current) =>
|
|
||||||
current.map((item) =>
|
|
||||||
item.id === rule.id ? { ...item, target: nextValue } : item,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<select
|
|
||||||
className={`${baseInputClass} col-span-12 self-end md:col-span-3`}
|
|
||||||
value={rule.property}
|
|
||||||
title="Hedef alanın hangi özelliği değişecek?"
|
|
||||||
onChange={(event) =>
|
|
||||||
setToggleRules((current) =>
|
|
||||||
current.map((item) =>
|
|
||||||
item.id === rule.id
|
|
||||||
? {
|
|
||||||
...item,
|
|
||||||
property: event.target.value as ToggleRule['property'],
|
|
||||||
}
|
|
||||||
: item,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<option value="disabled">disabled</option>
|
|
||||||
<option value="readOnly">readOnly</option>
|
|
||||||
<option value="visible">visible</option>
|
|
||||||
</select>
|
|
||||||
<div className="col-span-12 md:col-span-4">
|
|
||||||
{renderLabeledFieldSelect('Eğer: kaynak field', rule.source, (nextValue) =>
|
|
||||||
setToggleRules((current) =>
|
|
||||||
current.map((item) =>
|
|
||||||
item.id === rule.id ? { ...item, source: nextValue } : item,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
shape="circle"
|
|
||||||
variant="plain"
|
|
||||||
icon={<FaTrash />}
|
|
||||||
onClick={() =>
|
|
||||||
setToggleRules((current) => current.filter((item) => item.id !== rule.id))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-12 gap-2 mt-2">
|
|
||||||
<select
|
|
||||||
className={`${baseInputClass} col-span-4`}
|
|
||||||
value={rule.operator}
|
|
||||||
title="Kaynak field için koşul operatörü."
|
|
||||||
onChange={(event) =>
|
|
||||||
setToggleRules((current) =>
|
|
||||||
current.map((item) =>
|
|
||||||
item.id === rule.id
|
|
||||||
? {
|
|
||||||
...item,
|
|
||||||
operator: event.target.value as ToggleRule['operator'],
|
|
||||||
}
|
|
||||||
: item,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<option value="empty">empty</option>
|
|
||||||
<option value="notEmpty">notEmpty</option>
|
|
||||||
<option value="equals">equals</option>
|
|
||||||
<option value="notEquals">notEquals</option>
|
|
||||||
</select>
|
|
||||||
<input
|
|
||||||
className={`${baseInputClass} col-span-12 md:col-span-4`}
|
|
||||||
value={rule.value}
|
|
||||||
disabled={rule.operator === 'empty' || rule.operator === 'notEmpty'}
|
|
||||||
onChange={(event) =>
|
|
||||||
setToggleRules((current) =>
|
|
||||||
current.map((item) =>
|
|
||||||
item.id === rule.id ? { ...item, value: event.target.value } : item,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
placeholder="Koşul değeri"
|
|
||||||
title="Eşitse/Eşit değilse durumunda karşılaştırılacak değer."
|
|
||||||
/>
|
|
||||||
<select
|
|
||||||
className={`${baseInputClass} col-span-12 md:col-span-4`}
|
|
||||||
value={String(rule.whenTrue)}
|
|
||||||
title="Koşul doğruysa hedef property hangi değeri alsın?"
|
|
||||||
onChange={(event) =>
|
|
||||||
setToggleRules((current) =>
|
|
||||||
current.map((item) =>
|
|
||||||
item.id === rule.id
|
|
||||||
? { ...item, whenTrue: event.target.value === 'true' }
|
|
||||||
: item,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<option value="true">true</option>
|
|
||||||
<option value="false">false</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="rounded border border-gray-200 dark:border-gray-700 p-3">
|
|
||||||
<div
|
|
||||||
className="mb-3 text-sm font-semibold"
|
|
||||||
title="Start/End tarih alanlarından gün farkını hesaplayıp hedef field'a yazar. Örnek: StartDate + EndDate -> TotalDays."
|
title="Start/End tarih alanlarından gün farkını hesaplayıp hedef field'a yazar. Örnek: StartDate + EndDate -> TotalDays."
|
||||||
>
|
>
|
||||||
4. Tarih Farkı
|
3. Tarih ve Saat Farkı
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
size="sm"
|
|
||||||
onClick={fillDateDefaults}
|
|
||||||
title="Eski kullanım: StartDate ve EndDate değişince TotalDays alanını gün farkı olarak hesapla."
|
|
||||||
>
|
|
||||||
StartDate / EndDate / TotalDays
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-2">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-2">
|
||||||
{renderLabeledFieldSelect('Başlangıç tarihi', daysStartField, setDaysStartField)}
|
{renderLabeledFieldSelect('Başlangıç tarihi', daysStartField, setDaysStartField)}
|
||||||
{renderLabeledFieldSelect('Bitiş tarihi', daysEndField, setDaysEndField)}
|
{renderLabeledFieldSelect('Bitiş tarihi', daysEndField, setDaysEndField)}
|
||||||
{renderLabeledFieldSelect('Sonuç alanı', daysTargetField, setDaysTargetField)}
|
{renderLabeledFieldSelect('Sonuç alanı', daysTargetField, setDaysTargetField)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="rounded border border-gray-200 dark:border-gray-700 p-3">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-2 pt-2">
|
||||||
<div className="flex items-center justify-between gap-2 mb-3">
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
className="text-sm font-semibold"
|
|
||||||
title="Başlangıç ve bitiş saatinden saat farkını hesaplar. Bitiş başlangıçtan küçükse gece devri kabul edip 24 saat ekler."
|
|
||||||
>
|
|
||||||
5. Saat Farkı
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<label className="flex items-center gap-2 rounded border border-gray-100 px-2 py-1.5 text-sm dark:border-gray-700">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={timeDiffEnabled}
|
|
||||||
onChange={(event) => {
|
|
||||||
setTimeDiffEnabled(event.target.checked)
|
|
||||||
fillTimeDefaults()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span title="Eski kullanım: StartTime ve EndTime farkını hesapla; sonuç negatifse 24 saat ekleyip TotalHours alanına yaz.">
|
|
||||||
Aktif
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-2">
|
|
||||||
{renderLabeledFieldSelect('Başlangıç saati', timeStartField, setTimeStartField)}
|
{renderLabeledFieldSelect('Başlangıç saati', timeStartField, setTimeStartField)}
|
||||||
{renderLabeledFieldSelect('Bitiş saati', timeEndField, setTimeEndField)}
|
{renderLabeledFieldSelect('Bitiş saati', timeEndField, setTimeEndField)}
|
||||||
{renderLabeledFieldSelect('Sonuç alanı', timeTargetField, setTimeTargetField)}
|
{renderLabeledFieldSelect('Sonuç alanı', timeTargetField, setTimeTargetField)}
|
||||||
|
|
@ -1439,24 +1020,10 @@ function EditorScriptBuilderDialog({
|
||||||
className="mb-3 text-sm font-semibold"
|
className="mb-3 text-sm font-semibold"
|
||||||
title="Quantity ve UnitPrice ile TotalAmount hesaplar. Seçili kayıttan hesapla lookup kaydından değerleri alır; satır toplamını yenile mevcut form değerlerinden hesaplar."
|
title="Quantity ve UnitPrice ile TotalAmount hesaplar. Seçili kayıttan hesapla lookup kaydından değerleri alır; satır toplamını yenile mevcut form değerlerinden hesaplar."
|
||||||
>
|
>
|
||||||
6. Tutar Hesaplamaları
|
5. Tutar Hesaplamaları
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 gap-2 mb-3 md:grid-cols-2">
|
<div className="grid grid-cols-1 gap-2 mb-3 md:grid-cols-2">
|
||||||
<label className="flex items-start gap-2 rounded border border-gray-100 dark:border-gray-700 px-2 py-2 text-sm">
|
<label className="flex items-start gap-2 text-sm">
|
||||||
<input
|
|
||||||
className="mt-1"
|
|
||||||
type="checkbox"
|
|
||||||
checked={selectedItemAmountEnabled}
|
|
||||||
onChange={(event) => {
|
|
||||||
setSelectedItemAmountEnabled(event.target.checked)
|
|
||||||
fillAmountDefaults()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span title="Seçilen lookup/grid kaydından miktar, birim fiyat ve varsa birim bilgisini alır; toplamı bu değerlerle hesaplar.">
|
|
||||||
Seçili kayıttan miktar/fiyat al
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<label className="flex items-start gap-2 rounded border border-gray-100 dark:border-gray-700 px-2 py-2 text-sm">
|
|
||||||
<input
|
<input
|
||||||
className="mt-1"
|
className="mt-1"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
|
@ -1490,9 +1057,9 @@ function EditorScriptBuilderDialog({
|
||||||
<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
|
<div
|
||||||
className="mb-3 text-sm font-semibold"
|
className="mb-3 text-sm font-semibold"
|
||||||
title="Hâlâ karşılanmayan özel bir durum varsa burayı kullan. Servis çağrısı tek satırlık global servis çağrıları için, özel script ileri seviye serbest kod içindir."
|
title="Tek satırlık global servis çağrıları için kullan. Daha detaylı kodu sağdaki TypeScript script editor alanında düzenleyebilirsin."
|
||||||
>
|
>
|
||||||
7. Servis Çağrısı ve Özel Script
|
6. Servis Çağrısı
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
className={baseInputClass}
|
className={baseInputClass}
|
||||||
|
|
@ -1501,27 +1068,39 @@ function EditorScriptBuilderDialog({
|
||||||
placeholder="UiEvalService.ApiGenerateBackgroundWorkers();"
|
placeholder="UiEvalService.ApiGenerateBackgroundWorkers();"
|
||||||
title="Global servis çağrısı. Örnek: UiEvalService.ApiGenerateBackgroundWorkers();"
|
title="Global servis çağrısı. Örnek: UiEvalService.ApiGenerateBackgroundWorkers();"
|
||||||
/>
|
/>
|
||||||
<textarea
|
|
||||||
className="w-full min-h-[110px] mt-2 px-2 py-2 rounded border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800 text-sm font-mono text-gray-700 dark:text-gray-100 focus:outline-none focus:border-indigo-400"
|
|
||||||
value={customScript}
|
|
||||||
onChange={(event) => setCustomScript(event.target.value)}
|
|
||||||
placeholder="İleri seviye özel script"
|
|
||||||
title="İleri seviye serbest JavaScript. Mümkünse önce Koşullu Aksiyon kullan."
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="rounded border border-gray-200 dark:border-gray-700 p-3 flex flex-col min-h-[520px]">
|
<section className="rounded border border-gray-200 dark:border-gray-700 p-3 flex flex-col min-h-[520px]">
|
||||||
<div
|
<div
|
||||||
className="flex items-center gap-2 text-sm font-semibold mb-3"
|
className="flex items-center justify-between gap-2 text-sm font-semibold mb-3"
|
||||||
title="Boş editorScript ve seçim yoksa preview boş kalır. Seçim yaptıkça editorScript alanına yazılacak kod burada oluşur."
|
title="Seçim yaptıkça editorScript alanına yazılacak kod burada oluşur. Oluşan TypeScript kodunu kaydetmeden önce düzenleyebilirsin."
|
||||||
>
|
>
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
<FaCode />
|
<FaCode />
|
||||||
Script Önizleme
|
Script Önizleme
|
||||||
|
</span>
|
||||||
|
<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
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-h-[430px] overflow-hidden rounded border border-gray-200 dark:border-gray-700">
|
||||||
|
<Editor
|
||||||
|
height="100%"
|
||||||
|
language="typescript"
|
||||||
|
theme="vs-dark"
|
||||||
|
value={scriptEditorValue}
|
||||||
|
onChange={(nextValue) => setScriptEditorValue(nextValue || '')}
|
||||||
|
options={{
|
||||||
|
automaticLayout: true,
|
||||||
|
fontSize: 12,
|
||||||
|
minimap: { enabled: false },
|
||||||
|
scrollBeyondLastLine: false,
|
||||||
|
tabSize: 2,
|
||||||
|
wordWrap: 'on',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<pre className="flex-1 overflow-auto rounded bg-gray-50 dark:bg-gray-900 p-3 text-xs whitespace-pre-wrap">
|
|
||||||
{preview || 'Seçim yaptıkça script burada oluşacak.'}
|
|
||||||
</pre>
|
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</Dialog.Body>
|
</Dialog.Body>
|
||||||
|
|
@ -1535,7 +1114,7 @@ function EditorScriptBuilderDialog({
|
||||||
variant="solid"
|
variant="solid"
|
||||||
icon={<FaCheck />}
|
icon={<FaCheck />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onApply(preview)
|
onApply(scriptEditorValue)
|
||||||
onClose()
|
onClose()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -220,12 +220,7 @@ function JsonRowOpDialogEditForm({
|
||||||
invalid={errors.itemType && touched.itemType}
|
invalid={errors.itemType && touched.itemType}
|
||||||
errorMessage={errors.itemType}
|
errorMessage={errors.itemType}
|
||||||
>
|
>
|
||||||
<Field
|
<Field type="text" autoComplete="off" name="itemType" placeholder="Item Type">
|
||||||
type="text"
|
|
||||||
autoComplete="off"
|
|
||||||
name="itemType"
|
|
||||||
placeholder="Item Type"
|
|
||||||
>
|
|
||||||
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
||||||
<Select
|
<Select
|
||||||
field={field}
|
field={field}
|
||||||
|
|
@ -295,7 +290,7 @@ function JsonRowOpDialogEditForm({
|
||||||
<div>
|
<div>
|
||||||
<div className="font-bold">● Editor Script</div>
|
<div className="font-bold">● Editor Script</div>
|
||||||
<div className="ml-5 gap-2">
|
<div className="ml-5 gap-2">
|
||||||
<pre>{'• setFormData({...formData, Path: e.value});'}</pre>
|
<pre>{'• setFormData({ ...formData, Path: e.value });'}</pre>
|
||||||
<pre>{'• UiEvalService.ApiGenerateBackgroundWorkers();'}</pre>
|
<pre>{'• UiEvalService.ApiGenerateBackgroundWorkers();'}</pre>
|
||||||
<pre>
|
<pre>
|
||||||
{
|
{
|
||||||
|
|
@ -303,7 +298,17 @@ function JsonRowOpDialogEditForm({
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
<pre>
|
<pre>
|
||||||
{`• (() => {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});})(); `}
|
{`• (() => { 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});})(); `}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{
|
||||||
|
"• (() => { const p=e.component.option('selectedItem')||{}; const q=Math.round((parseFloat(p.Quantity)||0)*100); const u=Math.round((parseFloat(p.UnitPrice)||0)*100); setFormData({ ...formData, Quantity:q/100, UnitPrice:u/100, UomId:p.UomId, TotalAmount:Math.round((q*u)/100)/100 }); })();"
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{
|
||||||
|
'• (() => { const n={...formData,[e.dataField]:e.value}; const q=Math.round((parseFloat(n.Quantity)||0)*100); const u=Math.round((parseFloat(n.UnitPrice)||0)*100); setFormData({ ...n, TotalAmount:Math.round((q*u)/100)/100 }); })();'
|
||||||
|
}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -531,9 +536,7 @@ function JsonRowOpDialogEditForm({
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate(
|
label={translate('::ListForms.ListFormEdit.TabColumns')}
|
||||||
'::ListForms.ListFormEdit.TabColumns',
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<Field
|
<Field
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -775,10 +778,7 @@ function JsonRowOpDialogEditForm({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-1/12 ml-2 align-middle text-center">
|
<div className="w-1/12 ml-2 align-middle text-center">
|
||||||
<Field
|
<Field name={`items.${index}.isRequired`} component={Checkbox} />
|
||||||
name={`items.${index}.isRequired`}
|
|
||||||
component={Checkbox}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="w-1/12 ml-2">
|
<div className="w-1/12 ml-2">
|
||||||
<Field
|
<Field
|
||||||
|
|
|
||||||
|
|
@ -1079,7 +1079,7 @@ const WizardStep3 = ({
|
||||||
<div>
|
<div>
|
||||||
<div className="font-bold">● Editor Script</div>
|
<div className="font-bold">● Editor Script</div>
|
||||||
<div className="ml-5 gap-2">
|
<div className="ml-5 gap-2">
|
||||||
<pre>{'• setFormData({...formData, Path: e.value});'}</pre>
|
<pre>{'• setFormData({ ...formData, Path: e.value });'}</pre>
|
||||||
<pre>{'• UiEvalService.ApiGenerateBackgroundWorkers();'}</pre>
|
<pre>{'• UiEvalService.ApiGenerateBackgroundWorkers();'}</pre>
|
||||||
<pre>
|
<pre>
|
||||||
{
|
{
|
||||||
|
|
@ -1087,7 +1087,17 @@ const WizardStep3 = ({
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
<pre>
|
<pre>
|
||||||
{`• (() => {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});})(); `}
|
{`• (() => { 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});})(); `}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{
|
||||||
|
"• (() => { const p=e.component.option('selectedItem')||{}; const q=Math.round((parseFloat(p.Quantity)||0)*100); const u=Math.round((parseFloat(p.UnitPrice)||0)*100); setFormData({ ...formData, Quantity:q/100, UnitPrice:u/100, UomId:p.UomId, TotalAmount:Math.round((q*u)/100)/100 }); })();"
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
{
|
||||||
|
'• (() => { const n={...formData,[e.dataField]:e.value}; const q=Math.round((parseFloat(n.Quantity)||0)*100); const u=Math.round((parseFloat(n.UnitPrice)||0)*100); setFormData({ ...n, TotalAmount:Math.round((q*u)/100)/100 }); })();'
|
||||||
|
}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@ const Grid = (props: GridProps) => {
|
||||||
const gridRef = useRef<DataGridRef>()
|
const gridRef = useRef<DataGridRef>()
|
||||||
const refListFormCode = useRef('')
|
const refListFormCode = useRef('')
|
||||||
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
||||||
|
const editingFormDataRef = useRef<Record<string, any>>({})
|
||||||
// Edit popup state kaydetmeyi engellemek için flag
|
// Edit popup state kaydetmeyi engellemek için flag
|
||||||
const isEditingRef = useRef(false)
|
const isEditingRef = useRef(false)
|
||||||
|
|
||||||
|
|
@ -347,6 +348,7 @@ const Grid = (props: GridProps) => {
|
||||||
const onInitNewRow = useCallback(
|
const onInitNewRow = useCallback(
|
||||||
(e: any) => {
|
(e: any) => {
|
||||||
if (!gridDto?.columnFormats) {
|
if (!gridDto?.columnFormats) {
|
||||||
|
editingFormDataRef.current = { ...(e.data || {}) }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -436,6 +438,8 @@ const Grid = (props: GridProps) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editingFormDataRef.current = { ...(e.data || {}) }
|
||||||
})()
|
})()
|
||||||
},
|
},
|
||||||
[gridDto, searchParams, extraFilters, getNextSequenceValue],
|
[gridDto, searchParams, extraFilters, getNextSequenceValue],
|
||||||
|
|
@ -486,6 +490,7 @@ const Grid = (props: GridProps) => {
|
||||||
const onEditingStart = useCallback(
|
const onEditingStart = useCallback(
|
||||||
(e: DataGridTypes.EditingStartEvent<any, any>) => {
|
(e: DataGridTypes.EditingStartEvent<any, any>) => {
|
||||||
isEditingRef.current = true
|
isEditingRef.current = true
|
||||||
|
editingFormDataRef.current = { ...(e.data || {}) }
|
||||||
setMode('edit')
|
setMode('edit')
|
||||||
setIsPopupFullScreen(gridDto?.gridOptions.editingOptionDto?.popup?.fullScreen ?? false)
|
setIsPopupFullScreen(gridDto?.gridOptions.editingOptionDto?.popup?.fullScreen ?? false)
|
||||||
const columns = e.component.option('columns') as GridColumnData[]
|
const columns = e.component.option('columns') as GridColumnData[]
|
||||||
|
|
@ -1371,11 +1376,31 @@ const Grid = (props: GridProps) => {
|
||||||
colCount: 1,
|
colCount: 1,
|
||||||
onFieldDataChanged: (e) => {
|
onFieldDataChanged: (e) => {
|
||||||
if (e.dataField) {
|
if (e.dataField) {
|
||||||
|
const previousValue = editingFormDataRef.current?.[e.dataField]
|
||||||
|
editingFormDataRef.current = {
|
||||||
|
...(e.component?.option?.('formData') || {}),
|
||||||
|
}
|
||||||
|
|
||||||
const formItem = gridDto.gridOptions.editingFormDto
|
const formItem = gridDto.gridOptions.editingFormDto
|
||||||
.flatMap((group) => group.items || [])
|
.flatMap((group) => group.items || [])
|
||||||
.find((i) => i.dataField === e.dataField)
|
.find((i) => i.dataField === e.dataField)
|
||||||
if (formItem?.editorScript) {
|
if (formItem?.editorScript) {
|
||||||
try {
|
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 setFormData = (newData: any) => {
|
||||||
|
e.component?.option?.('formData', newData)
|
||||||
|
editingFormDataRef.current = { ...newData }
|
||||||
|
if (grid && rowIndex !== undefined && rowIndex >= 0) {
|
||||||
|
Object.keys(newData).forEach((field) => {
|
||||||
|
grid.cellValue(rowIndex, field, newData[field])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
eval(formItem.editorScript)
|
eval(formItem.editorScript)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Script exec error', err)
|
console.error('Script exec error', err)
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,7 @@ const Tree = (props: TreeProps) => {
|
||||||
const gridRef = useRef<TreeListRef>()
|
const gridRef = useRef<TreeListRef>()
|
||||||
const refListFormCode = useRef('')
|
const refListFormCode = useRef('')
|
||||||
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
||||||
|
const editingFormDataRef = useRef<Record<string, any>>({})
|
||||||
// Edit popup state kaydetmeyi engellemek için flag
|
// Edit popup state kaydetmeyi engellemek için flag
|
||||||
const isEditingRef = useRef(false)
|
const isEditingRef = useRef(false)
|
||||||
|
|
||||||
|
|
@ -381,6 +382,7 @@ const Tree = (props: TreeProps) => {
|
||||||
|
|
||||||
function onInitNewRow(e: any) {
|
function onInitNewRow(e: any) {
|
||||||
if (!gridDto?.columnFormats) {
|
if (!gridDto?.columnFormats) {
|
||||||
|
editingFormDataRef.current = { ...(e.data || {}) }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -444,6 +446,8 @@ const Tree = (props: TreeProps) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editingFormDataRef.current = { ...(e.data || {}) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -483,6 +487,7 @@ const Tree = (props: TreeProps) => {
|
||||||
const onEditingStart = useCallback(
|
const onEditingStart = useCallback(
|
||||||
(e: TreeListTypes.EditingStartEvent) => {
|
(e: TreeListTypes.EditingStartEvent) => {
|
||||||
isEditingRef.current = true
|
isEditingRef.current = true
|
||||||
|
editingFormDataRef.current = { ...(e.data || {}) }
|
||||||
setMode('edit')
|
setMode('edit')
|
||||||
setIsPopupFullScreen(gridDto?.gridOptions.editingOptionDto?.popup?.fullScreen ?? false)
|
setIsPopupFullScreen(gridDto?.gridOptions.editingOptionDto?.popup?.fullScreen ?? false)
|
||||||
const columns = e.component.option('columns') as GridColumnData[]
|
const columns = e.component.option('columns') as GridColumnData[]
|
||||||
|
|
@ -1029,11 +1034,31 @@ const Tree = (props: TreeProps) => {
|
||||||
colCount: 1,
|
colCount: 1,
|
||||||
onFieldDataChanged: (e) => {
|
onFieldDataChanged: (e) => {
|
||||||
if (e.dataField) {
|
if (e.dataField) {
|
||||||
|
const previousValue = editingFormDataRef.current?.[e.dataField]
|
||||||
|
editingFormDataRef.current = {
|
||||||
|
...(e.component?.option?.('formData') || {}),
|
||||||
|
}
|
||||||
|
|
||||||
const formItem = gridDto.gridOptions.editingFormDto
|
const formItem = gridDto.gridOptions.editingFormDto
|
||||||
.flatMap((group) => group.items || [])
|
.flatMap((group) => group.items || [])
|
||||||
.find((i) => i.dataField === e.dataField)
|
.find((i) => i.dataField === e.dataField)
|
||||||
if (formItem?.editorScript) {
|
if (formItem?.editorScript) {
|
||||||
try {
|
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 setFormData = (newData: any) => {
|
||||||
|
e.component?.option?.('formData', newData)
|
||||||
|
editingFormDataRef.current = { ...newData }
|
||||||
|
if (grid && rowIndex !== undefined && rowIndex >= 0) {
|
||||||
|
Object.keys(newData).forEach((field) => {
|
||||||
|
grid.cellValue(rowIndex, field, newData[field])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
eval(formItem.editorScript)
|
eval(formItem.editorScript)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Script exec error', err)
|
console.error('Script exec error', err)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue