@inspirer-dev/crm-dashboard 1.0.80 → 1.0.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/src/components/ButtonsBuilder/index.tsx +3 -3
- package/admin/src/components/CancelConditionsField/index.tsx +7 -6
- package/admin/src/components/RulesBuilder/index.tsx +9 -8
- package/admin/src/components/StepFlowBuilder/constants.ts +2 -0
- package/admin/src/components/StepFlowBuilder/index.tsx +1 -1
- package/admin/src/components/StepFlowBuilder/nodes/EventTriggerNode.tsx +29 -1
- package/admin/src/components/StepFlowBuilder/panels/EventTriggerConfig.tsx +97 -9
- package/admin/src/components/StepFlowBuilder/panels/NodeEditPanel.tsx +1 -1
- package/admin/src/components/StepFlowBuilder/panels/WaitConfig.tsx +1 -3
- package/admin/src/components/StepFlowBuilder/types.ts +2 -0
- package/admin/src/components/TriggerConfigField/index.tsx +11 -10
- package/admin/src/components/TriggerParamsField/constants.ts +109 -0
- package/admin/src/components/TriggerParamsField/index.tsx +292 -0
- package/admin/src/index.ts +26 -1
- package/admin/src/pages/HomePage/components/CampaignBuilder/index.tsx +4 -0
- package/admin/src/pages/HomePage/components/FlowBuilderTest.tsx +3 -3
- package/admin/src/pages/HomePage/components/SendTimeHeatMap.tsx +2 -2
- package/admin/src/pages/HomePage/components/StatsView.tsx +2 -2
- package/admin/src/translations/en.json +4 -1
- package/admin/src/translations/ru.json +4 -1
- package/admin/tsconfig.json +12 -1
- package/admin/tsconfig.tsbuildinfo +1 -1
- package/dist/_chunks/{en-DEUgX5uV.mjs → en-B5BgIROW.mjs} +3 -1
- package/dist/_chunks/{en-D2kTkBns.js → en-GKZo4lia.js} +3 -1
- package/dist/_chunks/{index-C-1xCfqJ.js → index-354YMebI.js} +152 -38
- package/dist/_chunks/{index-BydXrDhA.mjs → index-BKfFI_Jo.mjs} +2 -2
- package/dist/_chunks/{index-D1kMmlrA.js → index-BQcRoIJr.js} +5 -4
- package/dist/_chunks/{index-BJzk6xNb.mjs → index-BgwX1xYS.mjs} +7 -6
- package/dist/_chunks/{index-pJUyH-Qr.js → index-Cg0G_bTE.js} +2 -2
- package/dist/_chunks/index-CuMY0eo5.js +310 -0
- package/dist/_chunks/{index-unxa4Q_H.mjs → index-DKJtyGq7.mjs} +5 -4
- package/dist/_chunks/{index-BFRbyVHC.js → index-DRXMKPXI.js} +1 -1
- package/dist/_chunks/{index-Xa_4jez0.mjs → index-Dhj0KzCX.mjs} +5 -4
- package/dist/_chunks/{index-Dv1tGmDT.mjs → index-LXBoz7PC.mjs} +1 -1
- package/dist/_chunks/index-NvFKi6er.mjs +310 -0
- package/dist/_chunks/{index-BKrTVHBr.js → index-T7VXjklK.js} +5 -4
- package/dist/_chunks/{index-CWSibOAr.mjs → index-gR86Z3uh.mjs} +152 -38
- package/dist/_chunks/{index-CAwynvu7.js → index-xDSMnUMp.js} +7 -6
- package/dist/_chunks/{ru-BKzplvmu.js → ru-bj5iiIPr.js} +3 -1
- package/dist/_chunks/{ru-DOt1yfNm.mjs → ru-h22ZdPCS.mjs} +3 -1
- package/dist/admin/index.js +31 -7
- package/dist/admin/index.mjs +32 -8
- package/dist/server/index.js +5 -0
- package/dist/server/index.mjs +5 -0
- package/package.json +3 -3
- package/server/src/register.ts +6 -0
|
@@ -80,7 +80,7 @@ interface StrapiTheme {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
const useThemeColors = () => {
|
|
83
|
-
const theme = useTheme() as StrapiTheme;
|
|
83
|
+
const theme = useTheme() as unknown as StrapiTheme;
|
|
84
84
|
const isDark = theme?.colors?.neutral0 === '#212134';
|
|
85
85
|
|
|
86
86
|
return useMemo(() => ({
|
|
@@ -367,8 +367,8 @@ const ButtonsBuilder = forwardRef<HTMLDivElement, ButtonsBuilderProps>(
|
|
|
367
367
|
</Box>
|
|
368
368
|
)}
|
|
369
369
|
|
|
370
|
-
{error && <Field.Error
|
|
371
|
-
{hint && <Field.Hint
|
|
370
|
+
{error && <Field.Error />}
|
|
371
|
+
{hint && <Field.Hint />}
|
|
372
372
|
</Flex>
|
|
373
373
|
</Field.Root>
|
|
374
374
|
);
|
|
@@ -116,7 +116,8 @@ const ValueInput: React.FC<ValueInputProps> = ({ metric, value, operator, onChan
|
|
|
116
116
|
return (
|
|
117
117
|
<Flex gap={2} alignItems="center">
|
|
118
118
|
<Switch
|
|
119
|
-
|
|
119
|
+
onLabel=""
|
|
120
|
+
offLabel=""
|
|
120
121
|
checked={boolVal}
|
|
121
122
|
onChange={() => onChange(!boolVal)}
|
|
122
123
|
disabled={disabled}
|
|
@@ -161,7 +162,7 @@ const ValueInput: React.FC<ValueInputProps> = ({ metric, value, operator, onChan
|
|
|
161
162
|
<Box style={{ width: '120px' }}>
|
|
162
163
|
<SingleSelect
|
|
163
164
|
value={unit}
|
|
164
|
-
onChange={(newUnit: string) => onChange({ [newUnit]: numValue })}
|
|
165
|
+
onChange={(newUnit: string | number) => onChange({ [String(newUnit)]: numValue })}
|
|
165
166
|
disabled={disabled}
|
|
166
167
|
>
|
|
167
168
|
{CANCEL_TIME_UNITS.map((u) => (
|
|
@@ -215,7 +216,7 @@ const RuleRow: React.FC<RuleRowProps> = ({ rule, onUpdate, onDelete, disabled })
|
|
|
215
216
|
<Box style={{ flex: '1 1 200px', minWidth: '180px' }}>
|
|
216
217
|
<SingleSelect
|
|
217
218
|
value={rule.field}
|
|
218
|
-
onChange={(val: string) => handleFieldChange(val)}
|
|
219
|
+
onChange={(val: string | number) => handleFieldChange(String(val))}
|
|
219
220
|
disabled={disabled}
|
|
220
221
|
size="S"
|
|
221
222
|
>
|
|
@@ -230,7 +231,7 @@ const RuleRow: React.FC<RuleRowProps> = ({ rule, onUpdate, onDelete, disabled })
|
|
|
230
231
|
<Box style={{ width: '80px' }}>
|
|
231
232
|
<SingleSelect
|
|
232
233
|
value={rule.operator}
|
|
233
|
-
onChange={(val: string) => onUpdate({ ...rule, operator: val as RuleOperator })}
|
|
234
|
+
onChange={(val: string | number) => onUpdate({ ...rule, operator: String(val) as RuleOperator })}
|
|
234
235
|
disabled={disabled}
|
|
235
236
|
size="S"
|
|
236
237
|
>
|
|
@@ -386,8 +387,8 @@ const CancelConditionsField = forwardRef<HTMLDivElement, CancelConditionsFieldPr
|
|
|
386
387
|
</CardContent>
|
|
387
388
|
</Card>
|
|
388
389
|
|
|
389
|
-
{error && <Field.Error
|
|
390
|
-
{hint && <Field.Hint
|
|
390
|
+
{error && <Field.Error />}
|
|
391
|
+
{hint && <Field.Hint />}
|
|
391
392
|
</Flex>
|
|
392
393
|
</Field.Root>
|
|
393
394
|
);
|
|
@@ -58,7 +58,7 @@ interface StrapiTheme {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
const useThemeColors = () => {
|
|
61
|
-
const theme = useTheme() as StrapiTheme;
|
|
61
|
+
const theme = useTheme() as unknown as StrapiTheme;
|
|
62
62
|
const isDark = theme?.colors?.neutral0 === '#212134';
|
|
63
63
|
|
|
64
64
|
return useMemo(
|
|
@@ -124,8 +124,9 @@ const ValueInput: React.FC<ValueInputProps> = ({
|
|
|
124
124
|
return (
|
|
125
125
|
<Flex gap={2} alignItems="center">
|
|
126
126
|
<Switch
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
onLabel=""
|
|
128
|
+
offLabel=""
|
|
129
|
+
checked={boolVal}
|
|
129
130
|
onChange={() => onChange(!boolVal)}
|
|
130
131
|
disabled={disabled}
|
|
131
132
|
/>
|
|
@@ -141,7 +142,7 @@ const ValueInput: React.FC<ValueInputProps> = ({
|
|
|
141
142
|
return (
|
|
142
143
|
<SingleSelect
|
|
143
144
|
value={boolVal ? 'true' : 'false'}
|
|
144
|
-
onChange={(val: string) => onChange(val === 'true')}
|
|
145
|
+
onChange={(val: string | number) => onChange(val === 'true')}
|
|
145
146
|
disabled={disabled}
|
|
146
147
|
size="S"
|
|
147
148
|
>
|
|
@@ -176,7 +177,7 @@ const ValueInput: React.FC<ValueInputProps> = ({
|
|
|
176
177
|
<Box style={{ width: '120px' }}>
|
|
177
178
|
<SingleSelect
|
|
178
179
|
value={unit}
|
|
179
|
-
onChange={(val: string) => onChange(createTimeAgoValue(amount, val))}
|
|
180
|
+
onChange={(val: string | number) => onChange(createTimeAgoValue(amount, String(val)))}
|
|
180
181
|
disabled={disabled}
|
|
181
182
|
size="S"
|
|
182
183
|
>
|
|
@@ -195,7 +196,7 @@ const ValueInput: React.FC<ValueInputProps> = ({
|
|
|
195
196
|
return (
|
|
196
197
|
<SingleSelect
|
|
197
198
|
value={String(value || '')}
|
|
198
|
-
onChange={(val: string) => onChange(val)}
|
|
199
|
+
onChange={(val: string | number) => onChange(val)}
|
|
199
200
|
disabled={disabled}
|
|
200
201
|
size="S"
|
|
201
202
|
>
|
|
@@ -328,7 +329,7 @@ const RuleRow: React.FC<RuleRowProps> = ({ rule, onUpdate, onDelete, disabled })
|
|
|
328
329
|
<Box style={{ flex: '1 1 200px', minWidth: '180px' }}>
|
|
329
330
|
<SingleSelect
|
|
330
331
|
value={rule.field}
|
|
331
|
-
onChange={(val: string) => handleFieldChange(val)}
|
|
332
|
+
onChange={(val: string | number) => handleFieldChange(String(val))}
|
|
332
333
|
disabled={disabled}
|
|
333
334
|
size="S"
|
|
334
335
|
>
|
|
@@ -350,7 +351,7 @@ const RuleRow: React.FC<RuleRowProps> = ({ rule, onUpdate, onDelete, disabled })
|
|
|
350
351
|
<Box style={{ width: '75px' }}>
|
|
351
352
|
<SingleSelect
|
|
352
353
|
value={rule.operator}
|
|
353
|
-
onChange={(val: string) => handleOperatorChange(val as RuleOperator)}
|
|
354
|
+
onChange={(val: string | number) => handleOperatorChange(val as RuleOperator)}
|
|
354
355
|
disabled={disabled}
|
|
355
356
|
size="S"
|
|
356
357
|
>
|
|
@@ -474,7 +474,7 @@ const StepFlowBuilderInner = forwardRef<HTMLDivElement, StepFlowBuilderProps>(
|
|
|
474
474
|
const stepCount = steps.length;
|
|
475
475
|
|
|
476
476
|
const validationError = useMemo(() => {
|
|
477
|
-
if (!validation || validation.isValid) return
|
|
477
|
+
if (!validation || validation.isValid) return undefined;
|
|
478
478
|
const errorCount = validation.errors.length;
|
|
479
479
|
const errorWord = pluralize(errorCount, 'ошибка', 'ошибки', 'ошибок');
|
|
480
480
|
return `Поток содержит ${errorCount} ${errorWord} валидации. Исправьте все ошибки перед публикацией.`;
|
|
@@ -11,6 +11,8 @@ const EventTriggerNode: React.FC<NodeProps<FlowNodeData>> = ({ id, data, selecte
|
|
|
11
11
|
const eventTrigger = data.config.eventTrigger;
|
|
12
12
|
const events = eventTrigger?.events || [];
|
|
13
13
|
const logic = eventTrigger?.logic || 'or';
|
|
14
|
+
const timeout = eventTrigger?.timeout || 0;
|
|
15
|
+
const timeoutUnit = eventTrigger?.timeoutUnit || 'minutes';
|
|
14
16
|
const { hasError, hasWarning } = useNodeValidation(id);
|
|
15
17
|
|
|
16
18
|
const getEventLabel = () => {
|
|
@@ -19,6 +21,17 @@ const EventTriggerNode: React.FC<NodeProps<FlowNodeData>> = ({ id, data, selecte
|
|
|
19
21
|
return `${events.length} events (${logic.toUpperCase()})`;
|
|
20
22
|
};
|
|
21
23
|
|
|
24
|
+
const getTimeoutLabel = () => {
|
|
25
|
+
if (timeout <= 0) return null;
|
|
26
|
+
const unitShort: Record<string, string> = {
|
|
27
|
+
seconds: 's',
|
|
28
|
+
minutes: 'm',
|
|
29
|
+
hours: 'h',
|
|
30
|
+
days: 'd',
|
|
31
|
+
};
|
|
32
|
+
return `${timeout}${unitShort[timeoutUnit] || timeoutUnit}`;
|
|
33
|
+
};
|
|
34
|
+
|
|
22
35
|
return (
|
|
23
36
|
<div
|
|
24
37
|
style={{
|
|
@@ -106,7 +119,7 @@ const EventTriggerNode: React.FC<NodeProps<FlowNodeData>> = ({ id, data, selecte
|
|
|
106
119
|
background: 'rgba(255, 255, 255, 0.9)',
|
|
107
120
|
padding: '2px 10px',
|
|
108
121
|
borderRadius: 4,
|
|
109
|
-
maxWidth: 160,
|
|
122
|
+
maxWidth: getTimeoutLabel() ? 120 : 160,
|
|
110
123
|
overflow: 'hidden',
|
|
111
124
|
textOverflow: 'ellipsis',
|
|
112
125
|
whiteSpace: 'nowrap',
|
|
@@ -114,6 +127,21 @@ const EventTriggerNode: React.FC<NodeProps<FlowNodeData>> = ({ id, data, selecte
|
|
|
114
127
|
>
|
|
115
128
|
{getEventLabel()}
|
|
116
129
|
</span>
|
|
130
|
+
{getTimeoutLabel() && (
|
|
131
|
+
<span
|
|
132
|
+
style={{
|
|
133
|
+
fontSize: 10,
|
|
134
|
+
fontWeight: 600,
|
|
135
|
+
color: '#d97706',
|
|
136
|
+
background: 'rgba(255, 255, 255, 0.9)',
|
|
137
|
+
padding: '2px 8px',
|
|
138
|
+
borderRadius: 4,
|
|
139
|
+
whiteSpace: 'nowrap',
|
|
140
|
+
}}
|
|
141
|
+
>
|
|
142
|
+
⏱ {getTimeoutLabel()}
|
|
143
|
+
</span>
|
|
144
|
+
)}
|
|
117
145
|
</div>
|
|
118
146
|
</div>
|
|
119
147
|
<Handle
|
|
@@ -7,8 +7,12 @@ import {
|
|
|
7
7
|
ComboboxOption,
|
|
8
8
|
Badge,
|
|
9
9
|
Checkbox,
|
|
10
|
+
NumberInput,
|
|
11
|
+
SingleSelect,
|
|
12
|
+
SingleSelectOption,
|
|
10
13
|
} from '@strapi/design-system';
|
|
11
|
-
import type { FlowNodeData, EventTriggerLogic } from '../types';
|
|
14
|
+
import type { FlowNodeData, EventTriggerLogic, DurationUnit } from '../types';
|
|
15
|
+
import { DURATION_UNIT_OPTIONS } from '../constants';
|
|
12
16
|
import { useFlowTheme } from '../hooks/useFlowTheme';
|
|
13
17
|
|
|
14
18
|
const ANALYTICS_EVENTS: Record<string, { value: string; label: string }[]> = {
|
|
@@ -81,6 +85,8 @@ const EventTriggerConfig: React.FC<EventTriggerConfigProps> = ({ data, onUpdate,
|
|
|
81
85
|
const eventTrigger = data.config.eventTrigger || { events: [], logic: 'or' as EventTriggerLogic };
|
|
82
86
|
const events = eventTrigger.events || [];
|
|
83
87
|
const logic = eventTrigger.logic || 'or';
|
|
88
|
+
const timeout = eventTrigger.timeout || 0;
|
|
89
|
+
const timeoutUnit = eventTrigger.timeoutUnit || 'minutes';
|
|
84
90
|
|
|
85
91
|
const [inputText, setInputText] = useState('');
|
|
86
92
|
const justSelectedRef = useRef(false);
|
|
@@ -135,6 +141,30 @@ const EventTriggerConfig: React.FC<EventTriggerConfigProps> = ({ data, onUpdate,
|
|
|
135
141
|
});
|
|
136
142
|
};
|
|
137
143
|
|
|
144
|
+
const handleTimeoutChange = (value: number | undefined) => {
|
|
145
|
+
onUpdate({
|
|
146
|
+
config: {
|
|
147
|
+
...data.config,
|
|
148
|
+
eventTrigger: {
|
|
149
|
+
...eventTrigger,
|
|
150
|
+
timeout: value || 0,
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const handleTimeoutUnitChange = (value: string) => {
|
|
157
|
+
onUpdate({
|
|
158
|
+
config: {
|
|
159
|
+
...data.config,
|
|
160
|
+
eventTrigger: {
|
|
161
|
+
...eventTrigger,
|
|
162
|
+
timeoutUnit: value as DurationUnit,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
|
|
138
168
|
return (
|
|
139
169
|
<Flex direction="column" gap={4}>
|
|
140
170
|
<Box style={{ width: '100%' }}>
|
|
@@ -168,7 +198,8 @@ const EventTriggerConfig: React.FC<EventTriggerConfigProps> = ({ data, onUpdate,
|
|
|
168
198
|
disabled={disabled}
|
|
169
199
|
creatable
|
|
170
200
|
createMessage={(v: string) => `Использовать: "${v}"`}
|
|
171
|
-
onCreateOption={(v
|
|
201
|
+
onCreateOption={(v?: string) => {
|
|
202
|
+
if (!v) return;
|
|
172
203
|
handleAddEvent(v);
|
|
173
204
|
}}
|
|
174
205
|
>
|
|
@@ -322,6 +353,53 @@ const EventTriggerConfig: React.FC<EventTriggerConfigProps> = ({ data, onUpdate,
|
|
|
322
353
|
</Box>
|
|
323
354
|
)}
|
|
324
355
|
|
|
356
|
+
<Box style={{ width: '100%' }}>
|
|
357
|
+
<Typography
|
|
358
|
+
variant="sigma"
|
|
359
|
+
style={{
|
|
360
|
+
marginBottom: 10,
|
|
361
|
+
display: 'block',
|
|
362
|
+
fontSize: 11,
|
|
363
|
+
letterSpacing: '0.05em',
|
|
364
|
+
color: theme.textMuted,
|
|
365
|
+
}}
|
|
366
|
+
>
|
|
367
|
+
ТАЙМАУТ
|
|
368
|
+
</Typography>
|
|
369
|
+
<Flex gap={3} alignItems="center">
|
|
370
|
+
<Box style={{ flex: 1 }}>
|
|
371
|
+
<NumberInput
|
|
372
|
+
value={timeout}
|
|
373
|
+
onValueChange={handleTimeoutChange}
|
|
374
|
+
disabled={disabled}
|
|
375
|
+
step={1}
|
|
376
|
+
/>
|
|
377
|
+
</Box>
|
|
378
|
+
<Box style={{ width: 140 }}>
|
|
379
|
+
<SingleSelect
|
|
380
|
+
value={timeoutUnit}
|
|
381
|
+
onChange={(val: string | number) => handleTimeoutUnitChange(String(val))}
|
|
382
|
+
disabled={disabled}
|
|
383
|
+
>
|
|
384
|
+
{DURATION_UNIT_OPTIONS.map((opt) => (
|
|
385
|
+
<SingleSelectOption key={opt.value} value={opt.value}>
|
|
386
|
+
{opt.label}
|
|
387
|
+
</SingleSelectOption>
|
|
388
|
+
))}
|
|
389
|
+
</SingleSelect>
|
|
390
|
+
</Box>
|
|
391
|
+
</Flex>
|
|
392
|
+
<Typography
|
|
393
|
+
variant="pi"
|
|
394
|
+
textColor="neutral500"
|
|
395
|
+
style={{ marginTop: 8, display: 'block' }}
|
|
396
|
+
>
|
|
397
|
+
{timeout > 0
|
|
398
|
+
? `Если событие не произойдёт за ${timeout} ${timeoutUnit}, кампания продолжится автоматически`
|
|
399
|
+
: 'Установите 0 для бесконечного ожидания события'}
|
|
400
|
+
</Typography>
|
|
401
|
+
</Box>
|
|
402
|
+
|
|
325
403
|
<div
|
|
326
404
|
style={{
|
|
327
405
|
padding: 14,
|
|
@@ -340,15 +418,25 @@ const EventTriggerConfig: React.FC<EventTriggerConfigProps> = ({ data, onUpdate,
|
|
|
340
418
|
>
|
|
341
419
|
{events.length === 0 ? (
|
|
342
420
|
'Выберите события, которые должен совершить пользователь для продолжения кампании.'
|
|
343
|
-
) : logic === 'or' ? (
|
|
344
|
-
<>
|
|
345
|
-
Кампания продолжится, когда пользователь выполнит{' '}
|
|
346
|
-
<strong>любое</strong> из выбранных событий.
|
|
347
|
-
</>
|
|
348
421
|
) : (
|
|
349
422
|
<>
|
|
350
|
-
|
|
351
|
-
|
|
423
|
+
{logic === 'or' ? (
|
|
424
|
+
<>
|
|
425
|
+
Кампания продолжится, когда пользователь выполнит{' '}
|
|
426
|
+
<strong>любое</strong> из выбранных событий
|
|
427
|
+
</>
|
|
428
|
+
) : (
|
|
429
|
+
<>
|
|
430
|
+
Кампания продолжится только когда пользователь выполнит{' '}
|
|
431
|
+
<strong>все {events.length}</strong> выбранных события
|
|
432
|
+
</>
|
|
433
|
+
)}
|
|
434
|
+
{timeout > 0 && (
|
|
435
|
+
<>
|
|
436
|
+
{' '}или через <strong>{timeout} {timeoutUnit}</strong>
|
|
437
|
+
</>
|
|
438
|
+
)}
|
|
439
|
+
.
|
|
352
440
|
</>
|
|
353
441
|
)}
|
|
354
442
|
</Typography>
|
|
@@ -336,7 +336,7 @@ const NodeEditPanel: React.FC<ExtendedNodeEditPanelProps> = ({
|
|
|
336
336
|
<TextInput
|
|
337
337
|
value={name}
|
|
338
338
|
onChange={handleNameChange}
|
|
339
|
-
disabled={disabled
|
|
339
|
+
disabled={disabled}
|
|
340
340
|
placeholder="Enter step name..."
|
|
341
341
|
/>
|
|
342
342
|
</div>
|
|
@@ -61,7 +61,6 @@ const WaitConfig: React.FC<WaitConfigProps> = ({ data, onUpdate, disabled }) =>
|
|
|
61
61
|
onValueChange={handleDurationChange}
|
|
62
62
|
disabled={disabled}
|
|
63
63
|
step={1}
|
|
64
|
-
label=""
|
|
65
64
|
/>
|
|
66
65
|
</div>
|
|
67
66
|
</Box>
|
|
@@ -81,9 +80,8 @@ const WaitConfig: React.FC<WaitConfigProps> = ({ data, onUpdate, disabled }) =>
|
|
|
81
80
|
<div style={{ width: '100%' }}>
|
|
82
81
|
<SingleSelect
|
|
83
82
|
value={durationUnit}
|
|
84
|
-
onChange={handleUnitChange}
|
|
83
|
+
onChange={(val: string | number) => handleUnitChange(String(val))}
|
|
85
84
|
disabled={disabled}
|
|
86
|
-
label=""
|
|
87
85
|
>
|
|
88
86
|
{DURATION_UNIT_OPTIONS.map((opt) => (
|
|
89
87
|
<SingleSelectOption key={opt.value} value={opt.value}>
|
|
@@ -226,7 +226,7 @@ interface StrapiTheme {
|
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
const useThemeColors = () => {
|
|
229
|
-
const theme = useTheme() as StrapiTheme;
|
|
229
|
+
const theme = useTheme() as unknown as StrapiTheme;
|
|
230
230
|
const isDark = theme?.colors?.neutral0 === '#212134';
|
|
231
231
|
|
|
232
232
|
return useMemo(
|
|
@@ -524,7 +524,8 @@ const TriggerConfigField = forwardRef<HTMLDivElement, TriggerConfigFieldProps>(
|
|
|
524
524
|
disabled={disabled}
|
|
525
525
|
creatable
|
|
526
526
|
createMessage={(v: string) => `Use custom event: "${v}"`}
|
|
527
|
-
onCreateOption={(v
|
|
527
|
+
onCreateOption={(v?: string) => {
|
|
528
|
+
if (!v) return;
|
|
528
529
|
const eventValue = extractEventValue(v);
|
|
529
530
|
handleUpdate({ eventName: eventValue });
|
|
530
531
|
setInputText(getEventLabel(eventValue));
|
|
@@ -617,8 +618,8 @@ const TriggerConfigField = forwardRef<HTMLDivElement, TriggerConfigFieldProps>(
|
|
|
617
618
|
<Box style={{ width: 120 }}>
|
|
618
619
|
<SingleSelect
|
|
619
620
|
value={config.delayUnit || 'minutes'}
|
|
620
|
-
onChange={(val: string) =>
|
|
621
|
-
handleUpdate({ delayUnit: val as TriggerConfig['delayUnit'] })
|
|
621
|
+
onChange={(val: string | number) =>
|
|
622
|
+
handleUpdate({ delayUnit: String(val) as TriggerConfig['delayUnit'] })
|
|
622
623
|
}
|
|
623
624
|
disabled={disabled}
|
|
624
625
|
size="S"
|
|
@@ -743,8 +744,8 @@ const TriggerConfigField = forwardRef<HTMLDivElement, TriggerConfigFieldProps>(
|
|
|
743
744
|
<Box style={{ width: 160 }}>
|
|
744
745
|
<SingleSelect
|
|
745
746
|
value={String(config.scheduleEveryMinutes ?? 5)}
|
|
746
|
-
onChange={(val: string) =>
|
|
747
|
-
handleUpdate({ scheduleEveryMinutes: parseInt(val, 10) || 5 })
|
|
747
|
+
onChange={(val: string | number) =>
|
|
748
|
+
handleUpdate({ scheduleEveryMinutes: parseInt(String(val), 10) || 5 })
|
|
748
749
|
}
|
|
749
750
|
disabled={disabled}
|
|
750
751
|
size="S"
|
|
@@ -885,8 +886,8 @@ const TriggerConfigField = forwardRef<HTMLDivElement, TriggerConfigFieldProps>(
|
|
|
885
886
|
textColor="neutral500"
|
|
886
887
|
style={{ marginTop: 8, display: 'block' }}
|
|
887
888
|
>
|
|
888
|
-
Формат: минута час день месяц день_недели (напр., "0 12 * * *" = ежедневно в
|
|
889
|
-
UTC)
|
|
889
|
+
Формат: минута час день месяц день_недели (напр., "0 12 * * *" = ежедневно в
|
|
890
|
+
12:00 UTC)
|
|
890
891
|
</Typography>
|
|
891
892
|
)}
|
|
892
893
|
</Box>
|
|
@@ -894,8 +895,8 @@ const TriggerConfigField = forwardRef<HTMLDivElement, TriggerConfigFieldProps>(
|
|
|
894
895
|
</Box>
|
|
895
896
|
)}
|
|
896
897
|
|
|
897
|
-
{error && <Field.Error
|
|
898
|
-
{hint && <Field.Hint
|
|
898
|
+
{error && <Field.Error />}
|
|
899
|
+
{hint && <Field.Hint />}
|
|
899
900
|
</Flex>
|
|
900
901
|
</Field.Root>
|
|
901
902
|
);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export interface TriggerParamDef {
|
|
2
|
+
key: string;
|
|
3
|
+
label: string;
|
|
4
|
+
description: string;
|
|
5
|
+
unit: string;
|
|
6
|
+
min: number;
|
|
7
|
+
placeholder: string;
|
|
8
|
+
stageTypes: string[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const TRIGGER_PARAMS: TriggerParamDef[] = [
|
|
12
|
+
{
|
|
13
|
+
key: 'delaySeconds',
|
|
14
|
+
label: 'Задержка',
|
|
15
|
+
description: 'Задержка перед показом после срабатывания триггера',
|
|
16
|
+
unit: 'сек',
|
|
17
|
+
min: 0,
|
|
18
|
+
placeholder: '7',
|
|
19
|
+
stageTypes: ['side_hint', 'modal', 'retry'],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
key: 'timeOnSiteSeconds',
|
|
23
|
+
label: 'Время на сайте',
|
|
24
|
+
description: 'Мин. время на сайте для срабатывания',
|
|
25
|
+
unit: 'сек',
|
|
26
|
+
min: 0,
|
|
27
|
+
placeholder: '120',
|
|
28
|
+
stageTypes: ['exit_intent', 'side_hint', 'modal'],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
key: 'scrollThresholdPx',
|
|
32
|
+
label: 'Порог скролла',
|
|
33
|
+
description: 'Скролл в пикселях для срабатывания',
|
|
34
|
+
unit: 'px',
|
|
35
|
+
min: 0,
|
|
36
|
+
placeholder: '200',
|
|
37
|
+
stageTypes: ['side_hint'],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
key: 'idleSeconds',
|
|
41
|
+
label: 'Бездействие',
|
|
42
|
+
description: 'Время без кликов/скролла',
|
|
43
|
+
unit: 'сек',
|
|
44
|
+
min: 0,
|
|
45
|
+
placeholder: '15',
|
|
46
|
+
stageTypes: ['side_hint'],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
key: 'caseViewSeconds',
|
|
50
|
+
label: 'Просмотр кейса',
|
|
51
|
+
description: 'Время на странице кейса',
|
|
52
|
+
unit: 'сек',
|
|
53
|
+
min: 0,
|
|
54
|
+
placeholder: '12',
|
|
55
|
+
stageTypes: ['modal', 'side_hint'],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
key: 'activePlayMinutes',
|
|
59
|
+
label: 'Активная игра',
|
|
60
|
+
description: 'Время активной игры',
|
|
61
|
+
unit: 'мин',
|
|
62
|
+
min: 0,
|
|
63
|
+
placeholder: '10',
|
|
64
|
+
stageTypes: ['modal'],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
key: 'actionCountThreshold',
|
|
68
|
+
label: 'Порог действий',
|
|
69
|
+
description: 'Кол-во значимых действий + низкий баланс',
|
|
70
|
+
unit: '',
|
|
71
|
+
min: 0,
|
|
72
|
+
placeholder: '2',
|
|
73
|
+
stageTypes: ['modal'],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
key: 'balanceThresholdMultiplier',
|
|
77
|
+
label: 'Множитель баланса',
|
|
78
|
+
description: 'Множитель мин. цены кейса для "низкого баланса"',
|
|
79
|
+
unit: '×',
|
|
80
|
+
min: 0,
|
|
81
|
+
placeholder: '1.5',
|
|
82
|
+
stageTypes: ['modal', 'side_hint'],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
key: 'abandonedTimeoutMinutes',
|
|
86
|
+
label: 'Таймаут заброшенного депозита',
|
|
87
|
+
description: 'Через сколько минут депозит считается заброшенным',
|
|
88
|
+
unit: 'мин',
|
|
89
|
+
min: 1,
|
|
90
|
+
placeholder: '10',
|
|
91
|
+
stageTypes: ['retry'],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
key: 'minDepositAmount',
|
|
95
|
+
label: 'Мин. сумма депозита',
|
|
96
|
+
description: 'Подстановка в {{min_deposit}} в текстах',
|
|
97
|
+
unit: '₽',
|
|
98
|
+
min: 0,
|
|
99
|
+
placeholder: '250',
|
|
100
|
+
stageTypes: ['modal', 'side_hint', 'retry'],
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
export const STAGE_TYPE_LABELS: Record<string, string> = {
|
|
105
|
+
side_hint: 'Side-hint',
|
|
106
|
+
modal: 'Modal',
|
|
107
|
+
exit_intent: 'Exit intent',
|
|
108
|
+
retry: 'Retry',
|
|
109
|
+
};
|