@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.
@@ -0,0 +1,491 @@
1
+ const OPERATORS = [
2
+ { value: "$eq", label: "=", description: "Equals" },
3
+ { value: "$ne", label: "≠", description: "Not equals" },
4
+ { value: "$gt", label: ">", description: "Greater than" },
5
+ { value: "$lt", label: "<", description: "Less than" },
6
+ { value: "$gte", label: "≥", description: "Greater or equal" },
7
+ { value: "$lte", label: "≤", description: "Less or equal" },
8
+ { value: "$in", label: "in", description: "In list" },
9
+ { value: "$nin", label: "not in", description: "Not in list" },
10
+ { value: "$exists", label: "exists", description: "Field exists" },
11
+ { value: "$regex", label: "matches", description: "Regex match" }
12
+ ];
13
+ const numberOperators = ["$eq", "$ne", "$gt", "$lt", "$gte", "$lte"];
14
+ const timeAgoOperators = ["$gt", "$lt", "$gte", "$lte"];
15
+ const booleanOperators = ["$eq", "$ne"];
16
+ const stringOperators = ["$eq", "$ne", "$regex", "$in", "$nin"];
17
+ const existsOperators = ["$exists"];
18
+ const METRICS = [
19
+ {
20
+ key: "deposit_count",
21
+ label: "Deposit Count",
22
+ category: "counters",
23
+ valueType: "number",
24
+ description: "Total number of successful deposits",
25
+ operators: numberOperators
26
+ },
27
+ {
28
+ key: "case_open_count",
29
+ label: "Case Open Count",
30
+ category: "counters",
31
+ valueType: "number",
32
+ description: "Total cases opened",
33
+ operators: numberOperators
34
+ },
35
+ {
36
+ key: "upgrade_count",
37
+ label: "Upgrade Count",
38
+ category: "counters",
39
+ valueType: "number",
40
+ description: "Total upgrades performed",
41
+ operators: numberOperators
42
+ },
43
+ {
44
+ key: "battle_count",
45
+ label: "Battle Count",
46
+ category: "counters",
47
+ valueType: "number",
48
+ description: "Total battles created/participated",
49
+ operators: numberOperators
50
+ },
51
+ {
52
+ key: "contract_count",
53
+ label: "Contract Count",
54
+ category: "counters",
55
+ valueType: "number",
56
+ description: "Total contracts executed",
57
+ operators: numberOperators
58
+ },
59
+ {
60
+ key: "withdraw_count",
61
+ label: "Withdraw Count",
62
+ category: "counters",
63
+ valueType: "number",
64
+ description: "Total withdrawals",
65
+ operators: numberOperators
66
+ },
67
+ {
68
+ key: "session_count",
69
+ label: "Session Count",
70
+ category: "counters",
71
+ valueType: "number",
72
+ description: "Total sessions",
73
+ operators: numberOperators
74
+ },
75
+ {
76
+ key: "consecutive_active_days",
77
+ label: "Consecutive Active Days",
78
+ category: "counters",
79
+ valueType: "number",
80
+ description: "Days active in a row",
81
+ operators: numberOperators
82
+ },
83
+ {
84
+ key: "deposit_sum_total",
85
+ label: "Total Deposits Sum",
86
+ category: "money",
87
+ valueType: "number",
88
+ description: "Sum of all deposits in RUB",
89
+ operators: numberOperators
90
+ },
91
+ {
92
+ key: "withdraw_sum_total",
93
+ label: "Total Withdrawals Sum",
94
+ category: "money",
95
+ valueType: "number",
96
+ description: "Sum of all withdrawals",
97
+ operators: numberOperators
98
+ },
99
+ {
100
+ key: "win_sum_total",
101
+ label: "Total Wins Sum",
102
+ category: "money",
103
+ valueType: "number",
104
+ description: "Sum of all winnings",
105
+ operators: numberOperators
106
+ },
107
+ {
108
+ key: "avg_deposit",
109
+ label: "Average Deposit",
110
+ category: "money",
111
+ valueType: "number",
112
+ description: "Average deposit amount",
113
+ operators: numberOperators
114
+ },
115
+ {
116
+ key: "balance",
117
+ label: "Current Balance",
118
+ category: "money",
119
+ valueType: "number",
120
+ description: "Current account balance",
121
+ operators: numberOperators
122
+ },
123
+ {
124
+ key: "net_deposit",
125
+ label: "Net Deposit",
126
+ category: "money",
127
+ valueType: "number",
128
+ description: "Deposits minus withdrawals",
129
+ operators: numberOperators
130
+ },
131
+ {
132
+ key: "last_deposit_at",
133
+ label: "Last Deposit",
134
+ category: "time",
135
+ valueType: "time_ago",
136
+ description: "Time since last deposit",
137
+ operators: timeAgoOperators
138
+ },
139
+ {
140
+ key: "last_case_open_at",
141
+ label: "Last Case Open",
142
+ category: "time",
143
+ valueType: "time_ago",
144
+ description: "Time since last case opened",
145
+ operators: timeAgoOperators
146
+ },
147
+ {
148
+ key: "last_upgrade_at",
149
+ label: "Last Upgrade",
150
+ category: "time",
151
+ valueType: "time_ago",
152
+ description: "Time since last upgrade",
153
+ operators: timeAgoOperators
154
+ },
155
+ {
156
+ key: "last_battle_at",
157
+ label: "Last Battle",
158
+ category: "time",
159
+ valueType: "time_ago",
160
+ description: "Time since last battle",
161
+ operators: timeAgoOperators
162
+ },
163
+ {
164
+ key: "last_contract_at",
165
+ label: "Last Contract",
166
+ category: "time",
167
+ valueType: "time_ago",
168
+ description: "Time since last contract",
169
+ operators: timeAgoOperators
170
+ },
171
+ {
172
+ key: "last_withdraw_at",
173
+ label: "Last Withdrawal",
174
+ category: "time",
175
+ valueType: "time_ago",
176
+ description: "Time since last withdrawal",
177
+ operators: timeAgoOperators
178
+ },
179
+ {
180
+ key: "last_session_at",
181
+ label: "Last Activity",
182
+ category: "time",
183
+ valueType: "time_ago",
184
+ description: "Time since last session/activity",
185
+ operators: timeAgoOperators
186
+ },
187
+ {
188
+ key: "registered_at",
189
+ label: "Registration Date",
190
+ category: "time",
191
+ valueType: "time_ago",
192
+ description: "Time since registration",
193
+ operators: timeAgoOperators
194
+ },
195
+ {
196
+ key: "first_deposit_at",
197
+ label: "First Deposit Date",
198
+ category: "time",
199
+ valueType: "time_ago",
200
+ description: "Time since first deposit",
201
+ operators: [...timeAgoOperators, ...existsOperators]
202
+ },
203
+ {
204
+ key: "is_vip",
205
+ label: "Is VIP",
206
+ category: "status",
207
+ valueType: "boolean",
208
+ description: "VIP status",
209
+ operators: booleanOperators
210
+ },
211
+ {
212
+ key: "is_ftd",
213
+ label: "Is FTD (First-Time Depositor)",
214
+ category: "status",
215
+ valueType: "boolean",
216
+ description: "Has exactly 1 deposit",
217
+ operators: booleanOperators
218
+ },
219
+ {
220
+ key: "has_telegram",
221
+ label: "Has Telegram",
222
+ category: "status",
223
+ valueType: "boolean",
224
+ description: "Telegram connected",
225
+ operators: booleanOperators
226
+ },
227
+ {
228
+ key: "telegram_opt_out",
229
+ label: "Telegram Opt-Out",
230
+ category: "status",
231
+ valueType: "boolean",
232
+ description: "User opted out of TG messages",
233
+ operators: booleanOperators
234
+ },
235
+ {
236
+ key: "has_free_case",
237
+ label: "Has Free Case",
238
+ category: "status",
239
+ valueType: "boolean",
240
+ description: "Has available free case",
241
+ operators: booleanOperators
242
+ },
243
+ {
244
+ key: "lifecycle_stage",
245
+ label: "Lifecycle Stage",
246
+ category: "activity",
247
+ valueType: "string",
248
+ description: "New, Active, Sleep, Churn, Dead",
249
+ operators: stringOperators
250
+ },
251
+ {
252
+ key: "referral_source",
253
+ label: "Referral Source",
254
+ category: "activity",
255
+ valueType: "string",
256
+ description: "How user was acquired",
257
+ operators: stringOperators
258
+ },
259
+ {
260
+ key: "country",
261
+ label: "Country",
262
+ category: "activity",
263
+ valueType: "string",
264
+ description: "User country code",
265
+ operators: stringOperators
266
+ },
267
+ {
268
+ key: "timezone",
269
+ label: "Timezone",
270
+ category: "activity",
271
+ valueType: "string",
272
+ description: "User timezone",
273
+ operators: stringOperators
274
+ }
275
+ ];
276
+ const METRICS_BY_CATEGORY = METRICS.reduce(
277
+ (acc, metric) => {
278
+ if (!acc[metric.category]) {
279
+ acc[metric.category] = [];
280
+ }
281
+ acc[metric.category].push(metric);
282
+ return acc;
283
+ },
284
+ {}
285
+ );
286
+ const CATEGORY_LABELS = {
287
+ counters: "📊 Counters",
288
+ money: "💰 Money",
289
+ time: "⏰ Time-based",
290
+ status: "🏷️ Status",
291
+ activity: "📱 Activity & Profile"
292
+ };
293
+ const LIFECYCLE_STAGES = ["new", "active", "sleep", "churn", "dead"];
294
+ const TIME_UNITS = [
295
+ { value: "minutes_ago", label: "minutes ago" },
296
+ { value: "hours_ago", label: "hours ago" },
297
+ { value: "days_ago", label: "days ago" }
298
+ ];
299
+ const DEFAULT_RULE = {
300
+ field: "deposit_count",
301
+ operator: "$eq",
302
+ value: 0
303
+ };
304
+ let idCounter = 0;
305
+ const generateId = () => {
306
+ idCounter += 1;
307
+ return `rule_${Date.now()}_${idCounter}`;
308
+ };
309
+ const createEmptyRule = () => ({
310
+ id: generateId(),
311
+ ...DEFAULT_RULE
312
+ });
313
+ const createEmptyGroup = (logic = "$and") => ({
314
+ id: generateId(),
315
+ logic,
316
+ rules: [createEmptyRule()]
317
+ });
318
+ const createInitialConfig = () => ({
319
+ id: generateId(),
320
+ logic: "$and",
321
+ rules: []
322
+ });
323
+ const isRuleGroup = (item) => {
324
+ return "logic" in item && "rules" in item;
325
+ };
326
+ const getMetricDefinition = (key) => {
327
+ return METRICS.find((m) => m.key === key);
328
+ };
329
+ const isTimeAgoValue = (value) => {
330
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
331
+ return false;
332
+ }
333
+ return "hours_ago" in value || "days_ago" in value || "minutes_ago" in value;
334
+ };
335
+ const parseTimeAgoValue = (value) => {
336
+ if (!isTimeAgoValue(value)) {
337
+ return { amount: 24, unit: "hours_ago" };
338
+ }
339
+ if (value.minutes_ago !== void 0) {
340
+ return { amount: value.minutes_ago, unit: "minutes_ago" };
341
+ }
342
+ if (value.hours_ago !== void 0) {
343
+ return { amount: value.hours_ago, unit: "hours_ago" };
344
+ }
345
+ if (value.days_ago !== void 0) {
346
+ return { amount: value.days_ago, unit: "days_ago" };
347
+ }
348
+ return { amount: 24, unit: "hours_ago" };
349
+ };
350
+ const createTimeAgoValue = (amount, unit) => {
351
+ return { [unit]: amount };
352
+ };
353
+ const serializeConfig = (config) => {
354
+ return JSON.stringify(config, null, 2);
355
+ };
356
+ const deserializeConfig = (json) => {
357
+ if (!json) {
358
+ return createInitialConfig();
359
+ }
360
+ try {
361
+ const parsed = typeof json === "string" ? JSON.parse(json) : json;
362
+ if (parsed && typeof parsed === "object" && "logic" in parsed && "rules" in parsed) {
363
+ return parsed;
364
+ }
365
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
366
+ const rules = Object.entries(parsed).map(([field, condition]) => {
367
+ const condObj = condition;
368
+ const operator = Object.keys(condObj)[0];
369
+ const value = condObj[operator];
370
+ return {
371
+ id: generateId(),
372
+ field,
373
+ operator,
374
+ value
375
+ };
376
+ });
377
+ return {
378
+ id: generateId(),
379
+ logic: "$and",
380
+ rules
381
+ };
382
+ }
383
+ return createInitialConfig();
384
+ } catch {
385
+ return createInitialConfig();
386
+ }
387
+ };
388
+ const updateRuleInGroup = (group, ruleId, updater) => {
389
+ return {
390
+ ...group,
391
+ rules: group.rules.map((item) => {
392
+ if (isRuleGroup(item)) {
393
+ return updateRuleInGroup(item, ruleId, updater);
394
+ }
395
+ if (item.id === ruleId) {
396
+ return updater(item);
397
+ }
398
+ return item;
399
+ })
400
+ };
401
+ };
402
+ const deleteRuleFromGroup = (group, ruleId) => {
403
+ const filtered = group.rules.map((item) => {
404
+ if (isRuleGroup(item)) {
405
+ const updated = deleteRuleFromGroup(item, ruleId);
406
+ if (updated.rules.length === 0) {
407
+ return null;
408
+ }
409
+ return updated;
410
+ }
411
+ if (item.id === ruleId) {
412
+ return null;
413
+ }
414
+ return item;
415
+ }).filter(Boolean);
416
+ return { ...group, rules: filtered };
417
+ };
418
+ const addRuleToGroup = (group, groupId, rule) => {
419
+ if (group.id === groupId) {
420
+ return {
421
+ ...group,
422
+ rules: [...group.rules, rule]
423
+ };
424
+ }
425
+ return {
426
+ ...group,
427
+ rules: group.rules.map((item) => {
428
+ if (isRuleGroup(item)) {
429
+ return addRuleToGroup(item, groupId, rule);
430
+ }
431
+ return item;
432
+ })
433
+ };
434
+ };
435
+ const updateGroupLogic = (group, groupId, logic) => {
436
+ if (group.id === groupId) {
437
+ return { ...group, logic };
438
+ }
439
+ return {
440
+ ...group,
441
+ rules: group.rules.map((item) => {
442
+ if (isRuleGroup(item)) {
443
+ return updateGroupLogic(item, groupId, logic);
444
+ }
445
+ return item;
446
+ })
447
+ };
448
+ };
449
+ const deleteGroupFromParent = (group, groupId) => {
450
+ const filtered = group.rules.map((item) => {
451
+ if (isRuleGroup(item)) {
452
+ if (item.id === groupId) {
453
+ return null;
454
+ }
455
+ return deleteGroupFromParent(item, groupId);
456
+ }
457
+ return item;
458
+ }).filter(Boolean);
459
+ return { ...group, rules: filtered };
460
+ };
461
+ const countRules = (group) => {
462
+ return group.rules.reduce((count, item) => {
463
+ if (isRuleGroup(item)) {
464
+ return count + countRules(item);
465
+ }
466
+ return count + 1;
467
+ }, 0);
468
+ };
469
+ export {
470
+ CATEGORY_LABELS as C,
471
+ LIFECYCLE_STAGES as L,
472
+ METRICS_BY_CATEGORY as M,
473
+ OPERATORS as O,
474
+ TIME_UNITS as T,
475
+ createInitialConfig as a,
476
+ deleteGroupFromParent as b,
477
+ countRules as c,
478
+ deserializeConfig as d,
479
+ deleteRuleFromGroup as e,
480
+ updateRuleInGroup as f,
481
+ getMetricDefinition as g,
482
+ addRuleToGroup as h,
483
+ isRuleGroup as i,
484
+ createEmptyRule as j,
485
+ createEmptyGroup as k,
486
+ createTimeAgoValue as l,
487
+ generateId as m,
488
+ parseTimeAgoValue as p,
489
+ serializeConfig as s,
490
+ updateGroupLogic as u
491
+ };