@inspirer-dev/crm-dashboard 1.0.10 → 1.0.12

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.
@@ -1,844 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const jsxRuntime = require("react/jsx-runtime");
4
- const React = require("react");
5
- const designSystem = require("@strapi/design-system");
6
- const icons = require("@strapi/icons");
7
- const styled = require("styled-components");
8
- const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
9
- const React__default = /* @__PURE__ */ _interopDefault(React);
10
- const OPERATORS = [
11
- { value: "$eq", label: "=", description: "Equals" },
12
- { value: "$ne", label: "≠", description: "Not equals" },
13
- { value: "$gt", label: ">", description: "Greater than" },
14
- { value: "$lt", label: "<", description: "Less than" },
15
- { value: "$gte", label: "≥", description: "Greater or equal" },
16
- { value: "$lte", label: "≤", description: "Less or equal" },
17
- { value: "$in", label: "in", description: "In list" },
18
- { value: "$nin", label: "not in", description: "Not in list" },
19
- { value: "$exists", label: "exists", description: "Field exists" },
20
- { value: "$regex", label: "matches", description: "Regex match" }
21
- ];
22
- const numberOperators = ["$eq", "$ne", "$gt", "$lt", "$gte", "$lte"];
23
- const timeAgoOperators = ["$gt", "$lt", "$gte", "$lte"];
24
- const booleanOperators = ["$eq", "$ne"];
25
- const stringOperators = ["$eq", "$ne", "$regex", "$in", "$nin"];
26
- const existsOperators = ["$exists"];
27
- const METRICS = [
28
- {
29
- key: "deposit_count",
30
- label: "Deposit Count",
31
- category: "counters",
32
- valueType: "number",
33
- description: "Total number of successful deposits",
34
- operators: numberOperators
35
- },
36
- {
37
- key: "case_open_count",
38
- label: "Case Open Count",
39
- category: "counters",
40
- valueType: "number",
41
- description: "Total cases opened",
42
- operators: numberOperators
43
- },
44
- {
45
- key: "upgrade_count",
46
- label: "Upgrade Count",
47
- category: "counters",
48
- valueType: "number",
49
- description: "Total upgrades performed",
50
- operators: numberOperators
51
- },
52
- {
53
- key: "battle_count",
54
- label: "Battle Count",
55
- category: "counters",
56
- valueType: "number",
57
- description: "Total battles created/participated",
58
- operators: numberOperators
59
- },
60
- {
61
- key: "contract_count",
62
- label: "Contract Count",
63
- category: "counters",
64
- valueType: "number",
65
- description: "Total contracts executed",
66
- operators: numberOperators
67
- },
68
- {
69
- key: "withdraw_count",
70
- label: "Withdraw Count",
71
- category: "counters",
72
- valueType: "number",
73
- description: "Total withdrawals",
74
- operators: numberOperators
75
- },
76
- {
77
- key: "session_count",
78
- label: "Session Count",
79
- category: "counters",
80
- valueType: "number",
81
- description: "Total sessions",
82
- operators: numberOperators
83
- },
84
- {
85
- key: "consecutive_active_days",
86
- label: "Consecutive Active Days",
87
- category: "counters",
88
- valueType: "number",
89
- description: "Days active in a row",
90
- operators: numberOperators
91
- },
92
- {
93
- key: "deposit_sum_total",
94
- label: "Total Deposits Sum",
95
- category: "money",
96
- valueType: "number",
97
- description: "Sum of all deposits in RUB",
98
- operators: numberOperators
99
- },
100
- {
101
- key: "withdraw_sum_total",
102
- label: "Total Withdrawals Sum",
103
- category: "money",
104
- valueType: "number",
105
- description: "Sum of all withdrawals",
106
- operators: numberOperators
107
- },
108
- {
109
- key: "win_sum_total",
110
- label: "Total Wins Sum",
111
- category: "money",
112
- valueType: "number",
113
- description: "Sum of all winnings",
114
- operators: numberOperators
115
- },
116
- {
117
- key: "avg_deposit",
118
- label: "Average Deposit",
119
- category: "money",
120
- valueType: "number",
121
- description: "Average deposit amount",
122
- operators: numberOperators
123
- },
124
- {
125
- key: "balance",
126
- label: "Current Balance",
127
- category: "money",
128
- valueType: "number",
129
- description: "Current account balance",
130
- operators: numberOperators
131
- },
132
- {
133
- key: "net_deposit",
134
- label: "Net Deposit",
135
- category: "money",
136
- valueType: "number",
137
- description: "Deposits minus withdrawals",
138
- operators: numberOperators
139
- },
140
- {
141
- key: "last_deposit_at",
142
- label: "Last Deposit",
143
- category: "time",
144
- valueType: "time_ago",
145
- description: "Time since last deposit",
146
- operators: timeAgoOperators
147
- },
148
- {
149
- key: "last_case_open_at",
150
- label: "Last Case Open",
151
- category: "time",
152
- valueType: "time_ago",
153
- description: "Time since last case opened",
154
- operators: timeAgoOperators
155
- },
156
- {
157
- key: "last_upgrade_at",
158
- label: "Last Upgrade",
159
- category: "time",
160
- valueType: "time_ago",
161
- description: "Time since last upgrade",
162
- operators: timeAgoOperators
163
- },
164
- {
165
- key: "last_battle_at",
166
- label: "Last Battle",
167
- category: "time",
168
- valueType: "time_ago",
169
- description: "Time since last battle",
170
- operators: timeAgoOperators
171
- },
172
- {
173
- key: "last_contract_at",
174
- label: "Last Contract",
175
- category: "time",
176
- valueType: "time_ago",
177
- description: "Time since last contract",
178
- operators: timeAgoOperators
179
- },
180
- {
181
- key: "last_withdraw_at",
182
- label: "Last Withdrawal",
183
- category: "time",
184
- valueType: "time_ago",
185
- description: "Time since last withdrawal",
186
- operators: timeAgoOperators
187
- },
188
- {
189
- key: "last_session_at",
190
- label: "Last Activity",
191
- category: "time",
192
- valueType: "time_ago",
193
- description: "Time since last session/activity",
194
- operators: timeAgoOperators
195
- },
196
- {
197
- key: "registered_at",
198
- label: "Registration Date",
199
- category: "time",
200
- valueType: "time_ago",
201
- description: "Time since registration",
202
- operators: timeAgoOperators
203
- },
204
- {
205
- key: "first_deposit_at",
206
- label: "First Deposit Date",
207
- category: "time",
208
- valueType: "time_ago",
209
- description: "Time since first deposit",
210
- operators: [...timeAgoOperators, ...existsOperators]
211
- },
212
- {
213
- key: "is_vip",
214
- label: "Is VIP",
215
- category: "status",
216
- valueType: "boolean",
217
- description: "VIP status",
218
- operators: booleanOperators
219
- },
220
- {
221
- key: "is_ftd",
222
- label: "Is FTD (First-Time Depositor)",
223
- category: "status",
224
- valueType: "boolean",
225
- description: "Has exactly 1 deposit",
226
- operators: booleanOperators
227
- },
228
- {
229
- key: "has_telegram",
230
- label: "Has Telegram",
231
- category: "status",
232
- valueType: "boolean",
233
- description: "Telegram connected",
234
- operators: booleanOperators
235
- },
236
- {
237
- key: "telegram_opt_out",
238
- label: "Telegram Opt-Out",
239
- category: "status",
240
- valueType: "boolean",
241
- description: "User opted out of TG messages",
242
- operators: booleanOperators
243
- },
244
- {
245
- key: "has_free_case",
246
- label: "Has Free Case",
247
- category: "status",
248
- valueType: "boolean",
249
- description: "Has available free case",
250
- operators: booleanOperators
251
- },
252
- {
253
- key: "lifecycle_stage",
254
- label: "Lifecycle Stage",
255
- category: "activity",
256
- valueType: "string",
257
- description: "New, Active, Sleep, Churn, Dead",
258
- operators: stringOperators
259
- },
260
- {
261
- key: "referral_source",
262
- label: "Referral Source",
263
- category: "activity",
264
- valueType: "string",
265
- description: "How user was acquired",
266
- operators: stringOperators
267
- },
268
- {
269
- key: "country",
270
- label: "Country",
271
- category: "activity",
272
- valueType: "string",
273
- description: "User country code",
274
- operators: stringOperators
275
- },
276
- {
277
- key: "timezone",
278
- label: "Timezone",
279
- category: "activity",
280
- valueType: "string",
281
- description: "User timezone",
282
- operators: stringOperators
283
- }
284
- ];
285
- const METRICS_BY_CATEGORY = METRICS.reduce(
286
- (acc, metric) => {
287
- if (!acc[metric.category]) {
288
- acc[metric.category] = [];
289
- }
290
- acc[metric.category].push(metric);
291
- return acc;
292
- },
293
- {}
294
- );
295
- const CATEGORY_LABELS = {
296
- counters: "📊 Counters",
297
- money: "💰 Money",
298
- time: "⏰ Time-based",
299
- status: "🏷️ Status",
300
- activity: "📱 Activity & Profile"
301
- };
302
- const LIFECYCLE_STAGES = ["new", "active", "sleep", "churn", "dead"];
303
- const TIME_UNITS = [
304
- { value: "minutes_ago", label: "minutes ago" },
305
- { value: "hours_ago", label: "hours ago" },
306
- { value: "days_ago", label: "days ago" }
307
- ];
308
- const DEFAULT_RULE = {
309
- field: "deposit_count",
310
- operator: "$eq",
311
- value: 0
312
- };
313
- let idCounter = 0;
314
- const generateId = () => {
315
- idCounter += 1;
316
- return `rule_${Date.now()}_${idCounter}`;
317
- };
318
- const createEmptyRule = () => ({
319
- id: generateId(),
320
- ...DEFAULT_RULE
321
- });
322
- const createEmptyGroup = (logic = "$and") => ({
323
- id: generateId(),
324
- logic,
325
- rules: [createEmptyRule()]
326
- });
327
- const createInitialConfig = () => ({
328
- id: generateId(),
329
- logic: "$and",
330
- rules: []
331
- });
332
- const isRuleGroup = (item) => {
333
- return "logic" in item && "rules" in item;
334
- };
335
- const getMetricDefinition = (key) => {
336
- return METRICS.find((m) => m.key === key);
337
- };
338
- const isTimeAgoValue = (value) => {
339
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
340
- return false;
341
- }
342
- return "hours_ago" in value || "days_ago" in value || "minutes_ago" in value;
343
- };
344
- const parseTimeAgoValue = (value) => {
345
- if (!isTimeAgoValue(value)) {
346
- return { amount: 24, unit: "hours_ago" };
347
- }
348
- if (value.minutes_ago !== void 0) {
349
- return { amount: value.minutes_ago, unit: "minutes_ago" };
350
- }
351
- if (value.hours_ago !== void 0) {
352
- return { amount: value.hours_ago, unit: "hours_ago" };
353
- }
354
- if (value.days_ago !== void 0) {
355
- return { amount: value.days_ago, unit: "days_ago" };
356
- }
357
- return { amount: 24, unit: "hours_ago" };
358
- };
359
- const createTimeAgoValue = (amount, unit) => {
360
- return { [unit]: amount };
361
- };
362
- const serializeConfig = (config) => {
363
- return JSON.stringify(config, null, 2);
364
- };
365
- const deserializeConfig = (json) => {
366
- if (!json) {
367
- return createInitialConfig();
368
- }
369
- try {
370
- const parsed = typeof json === "string" ? JSON.parse(json) : json;
371
- if (parsed && typeof parsed === "object" && "logic" in parsed && "rules" in parsed) {
372
- return parsed;
373
- }
374
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
375
- const rules = Object.entries(parsed).map(([field, condition]) => {
376
- const condObj = condition;
377
- const operator = Object.keys(condObj)[0];
378
- const value = condObj[operator];
379
- return {
380
- id: generateId(),
381
- field,
382
- operator,
383
- value
384
- };
385
- });
386
- return {
387
- id: generateId(),
388
- logic: "$and",
389
- rules
390
- };
391
- }
392
- return createInitialConfig();
393
- } catch {
394
- return createInitialConfig();
395
- }
396
- };
397
- const updateRuleInGroup = (group, ruleId, updater) => {
398
- return {
399
- ...group,
400
- rules: group.rules.map((item) => {
401
- if (isRuleGroup(item)) {
402
- return updateRuleInGroup(item, ruleId, updater);
403
- }
404
- if (item.id === ruleId) {
405
- return updater(item);
406
- }
407
- return item;
408
- })
409
- };
410
- };
411
- const deleteRuleFromGroup = (group, ruleId) => {
412
- const filtered = group.rules.map((item) => {
413
- if (isRuleGroup(item)) {
414
- const updated = deleteRuleFromGroup(item, ruleId);
415
- if (updated.rules.length === 0) {
416
- return null;
417
- }
418
- return updated;
419
- }
420
- if (item.id === ruleId) {
421
- return null;
422
- }
423
- return item;
424
- }).filter(Boolean);
425
- return { ...group, rules: filtered };
426
- };
427
- const addRuleToGroup = (group, groupId, rule) => {
428
- if (group.id === groupId) {
429
- return {
430
- ...group,
431
- rules: [...group.rules, rule]
432
- };
433
- }
434
- return {
435
- ...group,
436
- rules: group.rules.map((item) => {
437
- if (isRuleGroup(item)) {
438
- return addRuleToGroup(item, groupId, rule);
439
- }
440
- return item;
441
- })
442
- };
443
- };
444
- const updateGroupLogic = (group, groupId, logic) => {
445
- if (group.id === groupId) {
446
- return { ...group, logic };
447
- }
448
- return {
449
- ...group,
450
- rules: group.rules.map((item) => {
451
- if (isRuleGroup(item)) {
452
- return updateGroupLogic(item, groupId, logic);
453
- }
454
- return item;
455
- })
456
- };
457
- };
458
- const deleteGroupFromParent = (group, groupId) => {
459
- const filtered = group.rules.map((item) => {
460
- if (isRuleGroup(item)) {
461
- if (item.id === groupId) {
462
- return null;
463
- }
464
- return deleteGroupFromParent(item, groupId);
465
- }
466
- return item;
467
- }).filter(Boolean);
468
- return { ...group, rules: filtered };
469
- };
470
- const countRules = (group) => {
471
- return group.rules.reduce((count, item) => {
472
- if (isRuleGroup(item)) {
473
- return count + countRules(item);
474
- }
475
- return count + 1;
476
- }, 0);
477
- };
478
- const ValueInput = ({ metric, value, operator, onChange, disabled }) => {
479
- if (operator === "$exists") {
480
- const boolVal = value === true || value === "true";
481
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
482
- /* @__PURE__ */ jsxRuntime.jsx(
483
- designSystem.Switch,
484
- {
485
- label: "",
486
- selected: boolVal,
487
- onChange: () => onChange(!boolVal),
488
- disabled
489
- }
490
- ),
491
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", children: boolVal ? "exists" : "not exists" })
492
- ] });
493
- }
494
- if (metric.valueType === "boolean") {
495
- const boolVal = value === true || value === "true";
496
- return /* @__PURE__ */ jsxRuntime.jsxs(
497
- designSystem.SingleSelect,
498
- {
499
- value: boolVal ? "true" : "false",
500
- onChange: (val) => onChange(val === "true"),
501
- disabled,
502
- size: "S",
503
- children: [
504
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "true", children: "Yes" }),
505
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "false", children: "No" })
506
- ]
507
- }
508
- );
509
- }
510
- if (metric.valueType === "time_ago") {
511
- const { amount, unit } = parseTimeAgoValue(value);
512
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
513
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "80px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
514
- designSystem.TextInput,
515
- {
516
- type: "number",
517
- value: String(amount),
518
- onChange: (e) => {
519
- const num = parseInt(e.target.value, 10) || 0;
520
- onChange(createTimeAgoValue(num, unit));
521
- },
522
- disabled,
523
- size: "S",
524
- "aria-label": "Time amount"
525
- }
526
- ) }),
527
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "120px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
528
- designSystem.SingleSelect,
529
- {
530
- value: unit,
531
- onChange: (val) => onChange(createTimeAgoValue(amount, val)),
532
- disabled,
533
- size: "S",
534
- children: TIME_UNITS.map((u) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: u.value, children: u.label }, u.value))
535
- }
536
- ) })
537
- ] });
538
- }
539
- if (metric.key === "lifecycle_stage") {
540
- return /* @__PURE__ */ jsxRuntime.jsx(
541
- designSystem.SingleSelect,
542
- {
543
- value: String(value || ""),
544
- onChange: (val) => onChange(val),
545
- disabled,
546
- size: "S",
547
- children: LIFECYCLE_STAGES.map((stage) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: stage, children: stage.charAt(0).toUpperCase() + stage.slice(1) }, stage))
548
- }
549
- );
550
- }
551
- if (metric.valueType === "number") {
552
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "120px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
553
- designSystem.TextInput,
554
- {
555
- type: "number",
556
- value: String(value ?? ""),
557
- onChange: (e) => {
558
- const num = parseFloat(e.target.value);
559
- onChange(isNaN(num) ? 0 : num);
560
- },
561
- disabled,
562
- size: "S",
563
- "aria-label": "Value"
564
- }
565
- ) });
566
- }
567
- if (operator === "$in" || operator === "$nin") {
568
- const arrVal = Array.isArray(value) ? value.join(", ") : String(value || "");
569
- return /* @__PURE__ */ jsxRuntime.jsx(
570
- designSystem.TextInput,
571
- {
572
- value: arrVal,
573
- onChange: (e) => {
574
- const arr = e.target.value.split(",").map((s) => s.trim()).filter(Boolean);
575
- onChange(arr);
576
- },
577
- placeholder: "value1, value2, ...",
578
- disabled,
579
- size: "S",
580
- "aria-label": "Values"
581
- }
582
- );
583
- }
584
- return /* @__PURE__ */ jsxRuntime.jsx(
585
- designSystem.TextInput,
586
- {
587
- value: String(value ?? ""),
588
- onChange: (e) => onChange(e.target.value),
589
- disabled,
590
- size: "S",
591
- "aria-label": "Value"
592
- }
593
- );
594
- };
595
- const RuleRow = ({ rule, onUpdate, onDelete, disabled }) => {
596
- const theme = styled.useTheme();
597
- const colors = theme?.colors;
598
- const metric = getMetricDefinition(rule.field);
599
- const availableOperators = metric?.operators || OPERATORS.map((o) => o.value);
600
- const handleFieldChange = (fieldKey) => {
601
- const newMetric = getMetricDefinition(fieldKey);
602
- let newValue = 0;
603
- if (newMetric?.valueType === "boolean") {
604
- newValue = false;
605
- } else if (newMetric?.valueType === "time_ago") {
606
- newValue = { hours_ago: 24 };
607
- } else if (newMetric?.valueType === "string") {
608
- newValue = "";
609
- }
610
- const newOp = newMetric?.operators[0] || "$eq";
611
- onUpdate({ ...rule, field: fieldKey, operator: newOp, value: newValue });
612
- };
613
- const handleOperatorChange = (op) => {
614
- let newValue = rule.value;
615
- if (op === "$exists") {
616
- newValue = true;
617
- } else if ((op === "$in" || op === "$nin") && !Array.isArray(rule.value)) {
618
- newValue = [];
619
- }
620
- onUpdate({ ...rule, operator: op, value: newValue });
621
- };
622
- return /* @__PURE__ */ jsxRuntime.jsxs(
623
- designSystem.Flex,
624
- {
625
- gap: 2,
626
- alignItems: "center",
627
- padding: 2,
628
- background: "neutral0",
629
- hasRadius: true,
630
- style: { border: `1px solid ${colors?.neutral200 || "#dcdce4"}` },
631
- children: [
632
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: "1 1 200px", minWidth: "180px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
633
- designSystem.SingleSelect,
634
- {
635
- value: rule.field,
636
- onChange: (val) => handleFieldChange(val),
637
- disabled,
638
- size: "S",
639
- children: Object.entries(METRICS_BY_CATEGORY).map(([category, metrics]) => /* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
640
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: `__header_${category}`, disabled: true, children: CATEGORY_LABELS[category] || category }),
641
- metrics.map((m) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: m.key, children: m.label }, m.key))
642
- ] }, category))
643
- }
644
- ) }),
645
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "100px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
646
- designSystem.SingleSelect,
647
- {
648
- value: rule.operator,
649
- onChange: (val) => handleOperatorChange(val),
650
- disabled,
651
- size: "S",
652
- children: OPERATORS.filter((op) => availableOperators.includes(op.value)).map((op) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: op.value, children: op.label }, op.value))
653
- }
654
- ) }),
655
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: "1 1 150px", minWidth: "120px" }, children: metric && /* @__PURE__ */ jsxRuntime.jsx(
656
- ValueInput,
657
- {
658
- metric,
659
- value: rule.value,
660
- operator: rule.operator,
661
- onChange: (val) => onUpdate({ ...rule, value: val }),
662
- disabled
663
- }
664
- ) }),
665
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: "Delete rule", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.IconButton, { onClick: onDelete, label: "Delete rule", variant: "ghost", disabled, children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}) }) })
666
- ]
667
- }
668
- );
669
- };
670
- const RuleGroupComponent = ({
671
- group,
672
- onUpdate,
673
- onDelete,
674
- depth = 0,
675
- disabled
676
- }) => {
677
- const theme = styled.useTheme();
678
- const colors = theme?.colors;
679
- const borderColors = {
680
- 0: colors?.primary200 || "#d9d8ff",
681
- 1: colors?.success200 || "#c6f0c2",
682
- 2: colors?.warning200 || "#fae7b9",
683
- 3: colors?.danger200 || "#f5c0b8"
684
- };
685
- const borderColor = borderColors[depth % 4] || borderColors[0];
686
- const handleAddRule = () => {
687
- onUpdate(addRuleToGroup(group, group.id, createEmptyRule()));
688
- };
689
- const handleAddGroup = () => {
690
- const newLogic = group.logic === "$and" ? "$or" : "$and";
691
- onUpdate(addRuleToGroup(group, group.id, createEmptyGroup(newLogic)));
692
- };
693
- const handleLogicToggle = () => {
694
- onUpdate(updateGroupLogic(group, group.id, group.logic === "$and" ? "$or" : "$and"));
695
- };
696
- const handleRuleUpdate = (ruleId, updatedRule) => {
697
- onUpdate(updateRuleInGroup(group, ruleId, () => updatedRule));
698
- };
699
- const handleRuleDelete = (ruleId) => {
700
- onUpdate(deleteRuleFromGroup(group, ruleId));
701
- };
702
- const handleNestedGroupUpdate = (nestedGroup) => {
703
- onUpdate({
704
- ...group,
705
- rules: group.rules.map(
706
- (item) => isRuleGroup(item) && item.id === nestedGroup.id ? nestedGroup : item
707
- )
708
- });
709
- };
710
- const handleNestedGroupDelete = (groupId) => {
711
- onUpdate(deleteGroupFromParent(group, groupId));
712
- };
713
- return /* @__PURE__ */ jsxRuntime.jsxs(
714
- designSystem.Box,
715
- {
716
- padding: 3,
717
- background: depth === 0 ? "neutral100" : "neutral0",
718
- hasRadius: true,
719
- style: {
720
- borderLeft: `3px solid ${borderColor}`,
721
- marginLeft: depth > 0 ? "12px" : 0
722
- },
723
- children: [
724
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, children: [
725
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
726
- /* @__PURE__ */ jsxRuntime.jsx(
727
- designSystem.Button,
728
- {
729
- variant: group.logic === "$and" ? "default" : "secondary",
730
- size: "S",
731
- onClick: handleLogicToggle,
732
- disabled,
733
- children: group.logic === "$and" ? "AND" : "OR"
734
- }
735
- ),
736
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: group.logic === "$and" ? "All conditions must match" : "Any condition can match" })
737
- ] }),
738
- depth > 0 && onDelete && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: "Delete group", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.IconButton, { onClick: onDelete, label: "Delete group", variant: "ghost", disabled, children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}) }) })
739
- ] }),
740
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, children: group.rules.map((item, index) => {
741
- if (isRuleGroup(item)) {
742
- return /* @__PURE__ */ jsxRuntime.jsx(
743
- RuleGroupComponent,
744
- {
745
- group: item,
746
- onUpdate: handleNestedGroupUpdate,
747
- onDelete: () => handleNestedGroupDelete(item.id),
748
- depth: depth + 1,
749
- disabled
750
- },
751
- item.id
752
- );
753
- }
754
- return /* @__PURE__ */ jsxRuntime.jsx(
755
- RuleRow,
756
- {
757
- rule: item,
758
- onUpdate: (updated) => handleRuleUpdate(item.id, updated),
759
- onDelete: () => handleRuleDelete(item.id),
760
- disabled
761
- },
762
- item.id
763
- );
764
- }) }),
765
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, marginTop: 3, children: [
766
- /* @__PURE__ */ jsxRuntime.jsx(
767
- designSystem.Button,
768
- {
769
- variant: "secondary",
770
- size: "S",
771
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
772
- onClick: handleAddRule,
773
- disabled,
774
- children: "Add condition"
775
- }
776
- ),
777
- depth < 2 && /* @__PURE__ */ jsxRuntime.jsxs(
778
- designSystem.Button,
779
- {
780
- variant: "tertiary",
781
- size: "S",
782
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Layout, {}),
783
- onClick: handleAddGroup,
784
- disabled,
785
- children: [
786
- "Add ",
787
- group.logic === "$and" ? "OR" : "AND",
788
- " group"
789
- ]
790
- }
791
- )
792
- ] })
793
- ]
794
- }
795
- );
796
- };
797
- const RulesBuilder = React.forwardRef(
798
- ({ name, value, onChange, intlLabel, disabled, error, required, hint }, ref) => {
799
- const [config, setConfig] = React.useState(() => deserializeConfig(value));
800
- const displayLabel = React.useMemo(() => {
801
- if (intlLabel?.defaultMessage && !intlLabel.defaultMessage.includes(".")) {
802
- return intlLabel.defaultMessage;
803
- }
804
- return "Segment Rules";
805
- }, [intlLabel]);
806
- const rulesCount = React.useMemo(() => countRules(config), [config]);
807
- const handleConfigUpdate = React.useCallback(
808
- (newConfig) => {
809
- setConfig(newConfig);
810
- onChange({ target: { name, value: serializeConfig(newConfig) } });
811
- },
812
- [name, onChange]
813
- );
814
- const handleClear = () => {
815
- handleConfigUpdate(createInitialConfig());
816
- };
817
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name, error, hint, required, children: [
818
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
819
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: displayLabel }),
820
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
821
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { children: [
822
- rulesCount,
823
- " rule",
824
- rulesCount !== 1 ? "s" : ""
825
- ] }),
826
- rulesCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "ghost", size: "S", onClick: handleClear, disabled, children: "Clear all" })
827
- ] })
828
- ] }),
829
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 2, ref, children: /* @__PURE__ */ jsxRuntime.jsx(
830
- RuleGroupComponent,
831
- {
832
- group: config,
833
- onUpdate: handleConfigUpdate,
834
- depth: 0,
835
- disabled
836
- }
837
- ) }),
838
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {}),
839
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
840
- ] });
841
- }
842
- );
843
- RulesBuilder.displayName = "RulesBuilder";
844
- exports.default = RulesBuilder;