@inspirer-dev/crm-dashboard 1.0.11 → 1.0.13

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