@inspirer-dev/crm-dashboard 1.0.74 → 1.0.76

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.
@@ -53,15 +53,72 @@ import {
53
53
  countRules,
54
54
  } from './utils';
55
55
 
56
+ interface StrapiTheme {
57
+ colors?: Record<string, string>;
58
+ }
59
+
60
+ const useThemeColors = () => {
61
+ const theme = useTheme() as StrapiTheme;
62
+ const isDark = theme?.colors?.neutral0 === '#212134';
63
+
64
+ return useMemo(
65
+ () => ({
66
+ isDark,
67
+ input: {
68
+ background: isDark ? '#181826' : '#ffffff',
69
+ color: isDark ? '#ffffff' : '#32324d',
70
+ border: isDark ? '#4a4a6a' : '#dcdce4',
71
+ },
72
+ ruleRow: {
73
+ background: isDark ? '#212134' : '#ffffff',
74
+ border: isDark ? '#32324d' : '#dcdce4',
75
+ hoverBorder: isDark ? '#4a4a6a' : '#b5b5c3',
76
+ },
77
+ group: {
78
+ rootBg: isDark
79
+ ? 'linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)'
80
+ : 'linear-gradient(135deg, #f8f9fa 0%, #f0f0ff 100%)',
81
+ nestedBg: isDark ? '#1a1a2e' : '#ffffff',
82
+ headerBg: isDark ? 'rgba(255,255,255,0.03)' : 'rgba(0,0,0,0.02)',
83
+ },
84
+ depthBorders: {
85
+ 0: isDark ? '#7b61ff' : '#7b61ff',
86
+ 1: isDark ? '#5cb176' : '#328048',
87
+ 2: isDark ? '#f29d41' : '#d9822b',
88
+ 3: isDark ? '#ee5e52' : '#d02b20',
89
+ },
90
+ emptyState: {
91
+ border: isDark ? '#32324d' : '#dcdce4',
92
+ iconBg: isDark ? '#2d2d4a' : '#f0f0ff',
93
+ },
94
+ }),
95
+ [isDark]
96
+ );
97
+ };
98
+
99
+ interface InputColors {
100
+ background: string;
101
+ color: string;
102
+ border: string;
103
+ }
104
+
56
105
  interface ValueInputProps {
57
106
  metric: MetricDefinition;
58
107
  value: RuleValue;
59
108
  operator: RuleOperator;
60
109
  onChange: (value: RuleValue) => void;
61
110
  disabled?: boolean;
111
+ inputColors: InputColors;
62
112
  }
63
113
 
64
- const ValueInput: React.FC<ValueInputProps> = ({ metric, value, operator, onChange, disabled }) => {
114
+ const ValueInput: React.FC<ValueInputProps> = ({
115
+ metric,
116
+ value,
117
+ operator,
118
+ onChange,
119
+ disabled,
120
+ inputColors,
121
+ }) => {
65
122
  if (operator === '$exists') {
66
123
  const boolVal = value === true || value === 'true';
67
124
  return (
@@ -109,6 +166,11 @@ const ValueInput: React.FC<ValueInputProps> = ({ metric, value, operator, onChan
109
166
  disabled={disabled}
110
167
  size="S"
111
168
  aria-label="Time amount"
169
+ style={{
170
+ color: inputColors.color,
171
+ background: inputColors.background,
172
+ borderColor: inputColors.border,
173
+ }}
112
174
  />
113
175
  </Box>
114
176
  <Box style={{ width: '120px' }}>
@@ -159,6 +221,11 @@ const ValueInput: React.FC<ValueInputProps> = ({ metric, value, operator, onChan
159
221
  disabled={disabled}
160
222
  size="S"
161
223
  aria-label="Value"
224
+ style={{
225
+ color: inputColors.color,
226
+ background: inputColors.background,
227
+ borderColor: inputColors.border,
228
+ }}
162
229
  />
163
230
  </Box>
164
231
  );
@@ -180,6 +247,11 @@ const ValueInput: React.FC<ValueInputProps> = ({ metric, value, operator, onChan
180
247
  disabled={disabled}
181
248
  size="S"
182
249
  aria-label="Values"
250
+ style={{
251
+ color: inputColors.color,
252
+ background: inputColors.background,
253
+ borderColor: inputColors.border,
254
+ }}
183
255
  />
184
256
  );
185
257
  }
@@ -191,6 +263,11 @@ const ValueInput: React.FC<ValueInputProps> = ({ metric, value, operator, onChan
191
263
  disabled={disabled}
192
264
  size="S"
193
265
  aria-label="Value"
266
+ style={{
267
+ color: inputColors.color,
268
+ background: inputColors.background,
269
+ borderColor: inputColors.border,
270
+ }}
194
271
  />
195
272
  );
196
273
  };
@@ -203,8 +280,8 @@ interface RuleRowProps {
203
280
  }
204
281
 
205
282
  const RuleRow: React.FC<RuleRowProps> = ({ rule, onUpdate, onDelete, disabled }) => {
206
- const theme = useTheme();
207
- const colors = (theme as Record<string, unknown>)?.colors as Record<string, string> | undefined;
283
+ const colors = useThemeColors();
284
+ const [hovered, setHovered] = useState(false);
208
285
  const metric = getMetricDefinition(rule.field);
209
286
  const availableOperators = metric?.operators || OPERATORS.map((o) => o.value);
210
287
 
@@ -236,10 +313,17 @@ const RuleRow: React.FC<RuleRowProps> = ({ rule, onUpdate, onDelete, disabled })
236
313
  <Flex
237
314
  gap={2}
238
315
  alignItems="center"
239
- padding={2}
240
- background="neutral0"
316
+ padding={3}
241
317
  hasRadius
242
- style={{ border: `1px solid ${colors?.neutral200 || '#dcdce4'}` }}
318
+ onMouseEnter={() => setHovered(true)}
319
+ onMouseLeave={() => setHovered(false)}
320
+ style={{
321
+ width: '80%',
322
+ background: colors.ruleRow.background,
323
+ border: `1px solid ${hovered ? colors.ruleRow.hoverBorder : colors.ruleRow.border}`,
324
+ boxShadow: hovered ? '0 2px 8px rgba(0,0,0,0.06)' : 'none',
325
+ transition: 'border-color 0.15s, box-shadow 0.15s',
326
+ }}
243
327
  >
244
328
  <Box style={{ flex: '1 1 200px', minWidth: '180px' }}>
245
329
  <SingleSelect
@@ -286,6 +370,7 @@ const RuleRow: React.FC<RuleRowProps> = ({ rule, onUpdate, onDelete, disabled })
286
370
  operator={rule.operator}
287
371
  onChange={(val) => onUpdate({ ...rule, value: val })}
288
372
  disabled={disabled}
373
+ inputColors={colors.input}
289
374
  />
290
375
  )}
291
376
  </Box>
@@ -314,16 +399,9 @@ const RuleGroupComponent: React.FC<RuleGroupComponentProps> = ({
314
399
  depth = 0,
315
400
  disabled,
316
401
  }) => {
317
- const theme = useTheme();
318
- const colors = (theme as Record<string, unknown>)?.colors as Record<string, string> | undefined;
319
-
320
- const borderColors: Record<number, string> = {
321
- 0: colors?.primary200 || '#d9d8ff',
322
- 1: colors?.success200 || '#c6f0c2',
323
- 2: colors?.warning200 || '#fae7b9',
324
- 3: colors?.danger200 || '#f5c0b8',
325
- };
326
- const borderColor = borderColors[depth % 4] || borderColors[0];
402
+ const colors = useThemeColors();
403
+ const borderColor =
404
+ colors.depthBorders[(depth % 4) as keyof typeof colors.depthBorders] || colors.depthBorders[0];
327
405
 
328
406
  const handleAddRule = () => {
329
407
  onUpdate(addRuleToGroup(group, group.id, createEmptyRule()));
@@ -361,15 +439,22 @@ const RuleGroupComponent: React.FC<RuleGroupComponentProps> = ({
361
439
 
362
440
  return (
363
441
  <Box
364
- padding={3}
365
- background={depth === 0 ? 'neutral100' : 'neutral0'}
442
+ padding={4}
366
443
  hasRadius
367
444
  style={{
445
+ background: depth === 0 ? colors.group.rootBg : colors.group.nestedBg,
368
446
  borderLeft: `3px solid ${borderColor}`,
369
447
  marginLeft: depth > 0 ? '12px' : 0,
370
448
  }}
371
449
  >
372
- <Flex justifyContent="space-between" alignItems="center" marginBottom={3}>
450
+ <Flex
451
+ justifyContent="space-between"
452
+ alignItems="center"
453
+ marginBottom={3}
454
+ padding={2}
455
+ hasRadius
456
+ style={{ background: colors.group.headerBg }}
457
+ >
373
458
  <Flex gap={2} alignItems="center">
374
459
  <Button
375
460
  variant={group.logic === '$and' ? 'default' : 'secondary'}
@@ -380,7 +465,9 @@ const RuleGroupComponent: React.FC<RuleGroupComponentProps> = ({
380
465
  {group.logic === '$and' ? 'AND' : 'OR'}
381
466
  </Button>
382
467
  <Typography variant="pi" textColor="neutral600">
383
- {group.logic === '$and' ? 'Все условия должны совпадать' : 'Любое условие может совпадать'}
468
+ {group.logic === '$and'
469
+ ? 'Все условия должны совпадать'
470
+ : 'Любое условие может совпадать'}
384
471
  </Typography>
385
472
  </Flex>
386
473
  {depth > 0 && onDelete && (
@@ -393,29 +480,69 @@ const RuleGroupComponent: React.FC<RuleGroupComponentProps> = ({
393
480
  </Flex>
394
481
 
395
482
  <Flex direction="column" gap={2}>
396
- {group.rules.map((item, index) => {
397
- if (isRuleGroup(item)) {
483
+ {group.rules.length === 0 ? (
484
+ <Box
485
+ padding={6}
486
+ hasRadius
487
+ style={{
488
+ border: `2px dashed ${colors.emptyState.border}`,
489
+ textAlign: 'center',
490
+ }}
491
+ >
492
+ <Box
493
+ style={{
494
+ width: '40px',
495
+ height: '40px',
496
+ borderRadius: '50%',
497
+ background: colors.emptyState.iconBg,
498
+ display: 'inline-flex',
499
+ alignItems: 'center',
500
+ justifyContent: 'center',
501
+ marginBottom: '8px',
502
+ }}
503
+ >
504
+ <Layout />
505
+ </Box>
506
+ <Typography
507
+ variant="omega"
508
+ textColor="neutral500"
509
+ style={{ display: 'block', marginTop: 4 }}
510
+ >
511
+ Условий пока нет
512
+ </Typography>
513
+ <Typography
514
+ variant="pi"
515
+ textColor="neutral400"
516
+ style={{ display: 'block', marginTop: 4 }}
517
+ >
518
+ Добавьте условие или группу для построения сегмента
519
+ </Typography>
520
+ </Box>
521
+ ) : (
522
+ group.rules.map((item) => {
523
+ if (isRuleGroup(item)) {
524
+ return (
525
+ <RuleGroupComponent
526
+ key={item.id}
527
+ group={item}
528
+ onUpdate={handleNestedGroupUpdate}
529
+ onDelete={() => handleNestedGroupDelete(item.id)}
530
+ depth={depth + 1}
531
+ disabled={disabled}
532
+ />
533
+ );
534
+ }
398
535
  return (
399
- <RuleGroupComponent
536
+ <RuleRow
400
537
  key={item.id}
401
- group={item}
402
- onUpdate={handleNestedGroupUpdate}
403
- onDelete={() => handleNestedGroupDelete(item.id)}
404
- depth={depth + 1}
538
+ rule={item}
539
+ onUpdate={(updated) => handleRuleUpdate(item.id, updated)}
540
+ onDelete={() => handleRuleDelete(item.id)}
405
541
  disabled={disabled}
406
542
  />
407
543
  );
408
- }
409
- return (
410
- <RuleRow
411
- key={item.id}
412
- rule={item}
413
- onUpdate={(updated) => handleRuleUpdate(item.id, updated)}
414
- onDelete={() => handleRuleDelete(item.id)}
415
- disabled={disabled}
416
- />
417
- );
418
- })}
544
+ })
545
+ )}
419
546
  </Flex>
420
547
 
421
548
  <Flex gap={2} marginTop={3}>
@@ -446,6 +573,7 @@ const RuleGroupComponent: React.FC<RuleGroupComponentProps> = ({
446
573
 
447
574
  const RulesBuilder = forwardRef<HTMLDivElement, RulesBuilderProps>(
448
575
  ({ name, value, onChange, intlLabel, disabled, error, required, hint }, ref) => {
576
+ const colors = useThemeColors();
449
577
  const [config, setConfig] = useState<RulesConfig>(() => deserializeConfig(value));
450
578
 
451
579
  useEffect(() => {
@@ -478,7 +606,12 @@ const RulesBuilder = forwardRef<HTMLDivElement, RulesBuilderProps>(
478
606
  <Flex justifyContent="space-between" alignItems="center">
479
607
  <Field.Label>{displayLabel}</Field.Label>
480
608
  <Flex gap={2} alignItems="center">
481
- <Badge backgroundColor="neutral100" textColor="neutral600">
609
+ <Badge
610
+ style={{
611
+ backgroundColor: colors.isDark ? '#2d2d4a' : '#f0f0ff',
612
+ color: colors.isDark ? '#a5a5ba' : '#666687',
613
+ }}
614
+ >
482
615
  {rulesCount} rule{rulesCount !== 1 ? 's' : ''}
483
616
  </Badge>
484
617
  {rulesCount > 0 && (
@@ -1 +1 @@
1
- {"fileNames":[],"fileInfos":[],"root":[],"options":{"esModuleInterop":true,"module":1,"noEmitOnError":true,"noImplicitThis":true,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"strict":false,"target":6},"errors":true,"version":"5.8.3"}
1
+ {"program":{"fileNames":[],"fileInfos":[],"root":[],"options":{"esModuleInterop":true,"module":1,"noEmitOnError":true,"noImplicitThis":true,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"strict":false,"target":6},"referencedMap":[],"exportedModulesMap":[]},"version":"5.4.4"}
@@ -1,10 +1,52 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import React, { forwardRef, useState, useEffect, useMemo, useCallback } from "react";
3
3
  import { Field, Flex, Badge, Button, Box, Typography, Tooltip, IconButton, SingleSelect, SingleSelectOption, Switch, TextInput } from "@strapi/design-system";
4
- import { Trash, Plus, Layout } from "@strapi/icons";
4
+ import { Trash, Layout, Plus } from "@strapi/icons";
5
5
  import { useTheme } from "styled-components";
6
6
  import { d as deserializeConfig, c as countRules, s as serializeConfig, a as createInitialConfig, i as isRuleGroup, u as updateGroupLogic, b as deleteGroupFromParent, g as getMetricDefinition, O as OPERATORS, M as METRICS_BY_CATEGORY, C as CATEGORY_LABELS, e as deleteRuleFromGroup, f as updateRuleInGroup, h as addRuleToGroup, j as createEmptyRule, k as createEmptyGroup, p as parseTimeAgoValue, l as createTimeAgoValue, T as TIME_UNITS, L as LIFECYCLE_STAGES } from "./utils-BHneBJ7U.mjs";
7
- const ValueInput = ({ metric, value, operator, onChange, disabled }) => {
7
+ const useThemeColors = () => {
8
+ const theme = useTheme();
9
+ const isDark = theme?.colors?.neutral0 === "#212134";
10
+ return useMemo(
11
+ () => ({
12
+ isDark,
13
+ input: {
14
+ background: isDark ? "#181826" : "#ffffff",
15
+ color: isDark ? "#ffffff" : "#32324d",
16
+ border: isDark ? "#4a4a6a" : "#dcdce4"
17
+ },
18
+ ruleRow: {
19
+ background: isDark ? "#212134" : "#ffffff",
20
+ border: isDark ? "#32324d" : "#dcdce4",
21
+ hoverBorder: isDark ? "#4a4a6a" : "#b5b5c3"
22
+ },
23
+ group: {
24
+ rootBg: isDark ? "linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)" : "linear-gradient(135deg, #f8f9fa 0%, #f0f0ff 100%)",
25
+ nestedBg: isDark ? "#1a1a2e" : "#ffffff",
26
+ headerBg: isDark ? "rgba(255,255,255,0.03)" : "rgba(0,0,0,0.02)"
27
+ },
28
+ depthBorders: {
29
+ 0: isDark ? "#7b61ff" : "#7b61ff",
30
+ 1: isDark ? "#5cb176" : "#328048",
31
+ 2: isDark ? "#f29d41" : "#d9822b",
32
+ 3: isDark ? "#ee5e52" : "#d02b20"
33
+ },
34
+ emptyState: {
35
+ border: isDark ? "#32324d" : "#dcdce4",
36
+ iconBg: isDark ? "#2d2d4a" : "#f0f0ff"
37
+ }
38
+ }),
39
+ [isDark]
40
+ );
41
+ };
42
+ const ValueInput = ({
43
+ metric,
44
+ value,
45
+ operator,
46
+ onChange,
47
+ disabled,
48
+ inputColors
49
+ }) => {
8
50
  if (operator === "$exists") {
9
51
  const boolVal = value === true || value === "true";
10
52
  return /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
@@ -50,7 +92,12 @@ const ValueInput = ({ metric, value, operator, onChange, disabled }) => {
50
92
  },
51
93
  disabled,
52
94
  size: "S",
53
- "aria-label": "Time amount"
95
+ "aria-label": "Time amount",
96
+ style: {
97
+ color: inputColors.color,
98
+ background: inputColors.background,
99
+ borderColor: inputColors.border
100
+ }
54
101
  }
55
102
  ) }),
56
103
  /* @__PURE__ */ jsx(Box, { style: { width: "120px" }, children: /* @__PURE__ */ jsx(
@@ -89,7 +136,12 @@ const ValueInput = ({ metric, value, operator, onChange, disabled }) => {
89
136
  },
90
137
  disabled,
91
138
  size: "S",
92
- "aria-label": "Value"
139
+ "aria-label": "Value",
140
+ style: {
141
+ color: inputColors.color,
142
+ background: inputColors.background,
143
+ borderColor: inputColors.border
144
+ }
93
145
  }
94
146
  ) });
95
147
  }
@@ -106,7 +158,12 @@ const ValueInput = ({ metric, value, operator, onChange, disabled }) => {
106
158
  placeholder: "value1, value2, ...",
107
159
  disabled,
108
160
  size: "S",
109
- "aria-label": "Values"
161
+ "aria-label": "Values",
162
+ style: {
163
+ color: inputColors.color,
164
+ background: inputColors.background,
165
+ borderColor: inputColors.border
166
+ }
110
167
  }
111
168
  );
112
169
  }
@@ -117,13 +174,18 @@ const ValueInput = ({ metric, value, operator, onChange, disabled }) => {
117
174
  onChange: (e) => onChange(e.target.value),
118
175
  disabled,
119
176
  size: "S",
120
- "aria-label": "Value"
177
+ "aria-label": "Value",
178
+ style: {
179
+ color: inputColors.color,
180
+ background: inputColors.background,
181
+ borderColor: inputColors.border
182
+ }
121
183
  }
122
184
  );
123
185
  };
124
186
  const RuleRow = ({ rule, onUpdate, onDelete, disabled }) => {
125
- const theme = useTheme();
126
- const colors = theme?.colors;
187
+ const colors = useThemeColors();
188
+ const [hovered, setHovered] = useState(false);
127
189
  const metric = getMetricDefinition(rule.field);
128
190
  const availableOperators = metric?.operators || OPERATORS.map((o) => o.value);
129
191
  const handleFieldChange = (fieldKey) => {
@@ -153,10 +215,17 @@ const RuleRow = ({ rule, onUpdate, onDelete, disabled }) => {
153
215
  {
154
216
  gap: 2,
155
217
  alignItems: "center",
156
- padding: 2,
157
- background: "neutral0",
218
+ padding: 3,
158
219
  hasRadius: true,
159
- style: { border: `1px solid ${colors?.neutral200 || "#dcdce4"}` },
220
+ onMouseEnter: () => setHovered(true),
221
+ onMouseLeave: () => setHovered(false),
222
+ style: {
223
+ width: "80%",
224
+ background: colors.ruleRow.background,
225
+ border: `1px solid ${hovered ? colors.ruleRow.hoverBorder : colors.ruleRow.border}`,
226
+ boxShadow: hovered ? "0 2px 8px rgba(0,0,0,0.06)" : "none",
227
+ transition: "border-color 0.15s, box-shadow 0.15s"
228
+ },
160
229
  children: [
161
230
  /* @__PURE__ */ jsx(Box, { style: { flex: "1 1 200px", minWidth: "180px" }, children: /* @__PURE__ */ jsx(
162
231
  SingleSelect,
@@ -188,7 +257,8 @@ const RuleRow = ({ rule, onUpdate, onDelete, disabled }) => {
188
257
  value: rule.value,
189
258
  operator: rule.operator,
190
259
  onChange: (val) => onUpdate({ ...rule, value: val }),
191
- disabled
260
+ disabled,
261
+ inputColors: colors.input
192
262
  }
193
263
  ) }),
194
264
  /* @__PURE__ */ jsx(Tooltip, { label: "Delete rule", children: /* @__PURE__ */ jsx(IconButton, { onClick: onDelete, label: "Delete rule", variant: "ghost", disabled, children: /* @__PURE__ */ jsx(Trash, {}) }) })
@@ -203,15 +273,8 @@ const RuleGroupComponent = ({
203
273
  depth = 0,
204
274
  disabled
205
275
  }) => {
206
- const theme = useTheme();
207
- const colors = theme?.colors;
208
- const borderColors = {
209
- 0: colors?.primary200 || "#d9d8ff",
210
- 1: colors?.success200 || "#c6f0c2",
211
- 2: colors?.warning200 || "#fae7b9",
212
- 3: colors?.danger200 || "#f5c0b8"
213
- };
214
- const borderColor = borderColors[depth % 4] || borderColors[0];
276
+ const colors = useThemeColors();
277
+ const borderColor = colors.depthBorders[depth % 4] || colors.depthBorders[0];
215
278
  const handleAddRule = () => {
216
279
  onUpdate(addRuleToGroup(group, group.id, createEmptyRule()));
217
280
  };
@@ -242,31 +305,88 @@ const RuleGroupComponent = ({
242
305
  return /* @__PURE__ */ jsxs(
243
306
  Box,
244
307
  {
245
- padding: 3,
246
- background: depth === 0 ? "neutral100" : "neutral0",
308
+ padding: 4,
247
309
  hasRadius: true,
248
310
  style: {
311
+ background: depth === 0 ? colors.group.rootBg : colors.group.nestedBg,
249
312
  borderLeft: `3px solid ${borderColor}`,
250
313
  marginLeft: depth > 0 ? "12px" : 0
251
314
  },
252
315
  children: [
253
- /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, children: [
254
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
255
- /* @__PURE__ */ jsx(
256
- Button,
257
- {
258
- variant: group.logic === "$and" ? "default" : "secondary",
259
- size: "S",
260
- onClick: handleLogicToggle,
261
- disabled,
262
- children: group.logic === "$and" ? "AND" : "OR"
263
- }
264
- ),
265
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: group.logic === "$and" ? "Все условия должны совпадать" : "Любое условие может совпадать" })
266
- ] }),
267
- depth > 0 && onDelete && /* @__PURE__ */ jsx(Tooltip, { label: "Delete group", children: /* @__PURE__ */ jsx(IconButton, { onClick: onDelete, label: "Delete group", variant: "ghost", disabled, children: /* @__PURE__ */ jsx(Trash, {}) }) })
268
- ] }),
269
- /* @__PURE__ */ jsx(Flex, { direction: "column", gap: 2, children: group.rules.map((item, index) => {
316
+ /* @__PURE__ */ jsxs(
317
+ Flex,
318
+ {
319
+ justifyContent: "space-between",
320
+ alignItems: "center",
321
+ marginBottom: 3,
322
+ padding: 2,
323
+ hasRadius: true,
324
+ style: { background: colors.group.headerBg },
325
+ children: [
326
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
327
+ /* @__PURE__ */ jsx(
328
+ Button,
329
+ {
330
+ variant: group.logic === "$and" ? "default" : "secondary",
331
+ size: "S",
332
+ onClick: handleLogicToggle,
333
+ disabled,
334
+ children: group.logic === "$and" ? "AND" : "OR"
335
+ }
336
+ ),
337
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: group.logic === "$and" ? "Все условия должны совпадать" : "Любое условие может совпадать" })
338
+ ] }),
339
+ depth > 0 && onDelete && /* @__PURE__ */ jsx(Tooltip, { label: "Delete group", children: /* @__PURE__ */ jsx(IconButton, { onClick: onDelete, label: "Delete group", variant: "ghost", disabled, children: /* @__PURE__ */ jsx(Trash, {}) }) })
340
+ ]
341
+ }
342
+ ),
343
+ /* @__PURE__ */ jsx(Flex, { direction: "column", gap: 2, children: group.rules.length === 0 ? /* @__PURE__ */ jsxs(
344
+ Box,
345
+ {
346
+ padding: 6,
347
+ hasRadius: true,
348
+ style: {
349
+ border: `2px dashed ${colors.emptyState.border}`,
350
+ textAlign: "center"
351
+ },
352
+ children: [
353
+ /* @__PURE__ */ jsx(
354
+ Box,
355
+ {
356
+ style: {
357
+ width: "40px",
358
+ height: "40px",
359
+ borderRadius: "50%",
360
+ background: colors.emptyState.iconBg,
361
+ display: "inline-flex",
362
+ alignItems: "center",
363
+ justifyContent: "center",
364
+ marginBottom: "8px"
365
+ },
366
+ children: /* @__PURE__ */ jsx(Layout, {})
367
+ }
368
+ ),
369
+ /* @__PURE__ */ jsx(
370
+ Typography,
371
+ {
372
+ variant: "omega",
373
+ textColor: "neutral500",
374
+ style: { display: "block", marginTop: 4 },
375
+ children: "Условий пока нет"
376
+ }
377
+ ),
378
+ /* @__PURE__ */ jsx(
379
+ Typography,
380
+ {
381
+ variant: "pi",
382
+ textColor: "neutral400",
383
+ style: { display: "block", marginTop: 4 },
384
+ children: "Добавьте условие или группу для построения сегмента"
385
+ }
386
+ )
387
+ ]
388
+ }
389
+ ) : group.rules.map((item) => {
270
390
  if (isRuleGroup(item)) {
271
391
  return /* @__PURE__ */ jsx(
272
392
  RuleGroupComponent,
@@ -325,6 +445,7 @@ const RuleGroupComponent = ({
325
445
  };
326
446
  const RulesBuilder = forwardRef(
327
447
  ({ name, value, onChange, intlLabel, disabled, error, required, hint }, ref) => {
448
+ const colors = useThemeColors();
328
449
  const [config, setConfig] = useState(() => deserializeConfig(value));
329
450
  useEffect(() => {
330
451
  setConfig(deserializeConfig(value));
@@ -350,11 +471,20 @@ const RulesBuilder = forwardRef(
350
471
  /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
351
472
  /* @__PURE__ */ jsx(Field.Label, { children: displayLabel }),
352
473
  /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
353
- /* @__PURE__ */ jsxs(Badge, { backgroundColor: "neutral100", textColor: "neutral600", children: [
354
- rulesCount,
355
- " rule",
356
- rulesCount !== 1 ? "s" : ""
357
- ] }),
474
+ /* @__PURE__ */ jsxs(
475
+ Badge,
476
+ {
477
+ style: {
478
+ backgroundColor: colors.isDark ? "#2d2d4a" : "#f0f0ff",
479
+ color: colors.isDark ? "#a5a5ba" : "#666687"
480
+ },
481
+ children: [
482
+ rulesCount,
483
+ " rule",
484
+ rulesCount !== 1 ? "s" : ""
485
+ ]
486
+ }
487
+ ),
358
488
  rulesCount > 0 && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "S", onClick: handleClear, disabled, children: "Очистить всё" })
359
489
  ] })
360
490
  ] }),
@@ -8,7 +8,49 @@ const styledComponents = require("styled-components");
8
8
  const utils = require("./utils-CAs_GSGv.js");
9
9
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
10
10
  const React__default = /* @__PURE__ */ _interopDefault(React);
11
- const ValueInput = ({ metric, value, operator, onChange, disabled }) => {
11
+ const useThemeColors = () => {
12
+ const theme = styledComponents.useTheme();
13
+ const isDark = theme?.colors?.neutral0 === "#212134";
14
+ return React.useMemo(
15
+ () => ({
16
+ isDark,
17
+ input: {
18
+ background: isDark ? "#181826" : "#ffffff",
19
+ color: isDark ? "#ffffff" : "#32324d",
20
+ border: isDark ? "#4a4a6a" : "#dcdce4"
21
+ },
22
+ ruleRow: {
23
+ background: isDark ? "#212134" : "#ffffff",
24
+ border: isDark ? "#32324d" : "#dcdce4",
25
+ hoverBorder: isDark ? "#4a4a6a" : "#b5b5c3"
26
+ },
27
+ group: {
28
+ rootBg: isDark ? "linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)" : "linear-gradient(135deg, #f8f9fa 0%, #f0f0ff 100%)",
29
+ nestedBg: isDark ? "#1a1a2e" : "#ffffff",
30
+ headerBg: isDark ? "rgba(255,255,255,0.03)" : "rgba(0,0,0,0.02)"
31
+ },
32
+ depthBorders: {
33
+ 0: isDark ? "#7b61ff" : "#7b61ff",
34
+ 1: isDark ? "#5cb176" : "#328048",
35
+ 2: isDark ? "#f29d41" : "#d9822b",
36
+ 3: isDark ? "#ee5e52" : "#d02b20"
37
+ },
38
+ emptyState: {
39
+ border: isDark ? "#32324d" : "#dcdce4",
40
+ iconBg: isDark ? "#2d2d4a" : "#f0f0ff"
41
+ }
42
+ }),
43
+ [isDark]
44
+ );
45
+ };
46
+ const ValueInput = ({
47
+ metric,
48
+ value,
49
+ operator,
50
+ onChange,
51
+ disabled,
52
+ inputColors
53
+ }) => {
12
54
  if (operator === "$exists") {
13
55
  const boolVal = value === true || value === "true";
14
56
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
@@ -54,7 +96,12 @@ const ValueInput = ({ metric, value, operator, onChange, disabled }) => {
54
96
  },
55
97
  disabled,
56
98
  size: "S",
57
- "aria-label": "Time amount"
99
+ "aria-label": "Time amount",
100
+ style: {
101
+ color: inputColors.color,
102
+ background: inputColors.background,
103
+ borderColor: inputColors.border
104
+ }
58
105
  }
59
106
  ) }),
60
107
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "120px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -93,7 +140,12 @@ const ValueInput = ({ metric, value, operator, onChange, disabled }) => {
93
140
  },
94
141
  disabled,
95
142
  size: "S",
96
- "aria-label": "Value"
143
+ "aria-label": "Value",
144
+ style: {
145
+ color: inputColors.color,
146
+ background: inputColors.background,
147
+ borderColor: inputColors.border
148
+ }
97
149
  }
98
150
  ) });
99
151
  }
@@ -110,7 +162,12 @@ const ValueInput = ({ metric, value, operator, onChange, disabled }) => {
110
162
  placeholder: "value1, value2, ...",
111
163
  disabled,
112
164
  size: "S",
113
- "aria-label": "Values"
165
+ "aria-label": "Values",
166
+ style: {
167
+ color: inputColors.color,
168
+ background: inputColors.background,
169
+ borderColor: inputColors.border
170
+ }
114
171
  }
115
172
  );
116
173
  }
@@ -121,13 +178,18 @@ const ValueInput = ({ metric, value, operator, onChange, disabled }) => {
121
178
  onChange: (e) => onChange(e.target.value),
122
179
  disabled,
123
180
  size: "S",
124
- "aria-label": "Value"
181
+ "aria-label": "Value",
182
+ style: {
183
+ color: inputColors.color,
184
+ background: inputColors.background,
185
+ borderColor: inputColors.border
186
+ }
125
187
  }
126
188
  );
127
189
  };
128
190
  const RuleRow = ({ rule, onUpdate, onDelete, disabled }) => {
129
- const theme = styledComponents.useTheme();
130
- const colors = theme?.colors;
191
+ const colors = useThemeColors();
192
+ const [hovered, setHovered] = React.useState(false);
131
193
  const metric = utils.getMetricDefinition(rule.field);
132
194
  const availableOperators = metric?.operators || utils.OPERATORS.map((o) => o.value);
133
195
  const handleFieldChange = (fieldKey) => {
@@ -157,10 +219,17 @@ const RuleRow = ({ rule, onUpdate, onDelete, disabled }) => {
157
219
  {
158
220
  gap: 2,
159
221
  alignItems: "center",
160
- padding: 2,
161
- background: "neutral0",
222
+ padding: 3,
162
223
  hasRadius: true,
163
- style: { border: `1px solid ${colors?.neutral200 || "#dcdce4"}` },
224
+ onMouseEnter: () => setHovered(true),
225
+ onMouseLeave: () => setHovered(false),
226
+ style: {
227
+ width: "80%",
228
+ background: colors.ruleRow.background,
229
+ border: `1px solid ${hovered ? colors.ruleRow.hoverBorder : colors.ruleRow.border}`,
230
+ boxShadow: hovered ? "0 2px 8px rgba(0,0,0,0.06)" : "none",
231
+ transition: "border-color 0.15s, box-shadow 0.15s"
232
+ },
164
233
  children: [
165
234
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: "1 1 200px", minWidth: "180px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
166
235
  designSystem.SingleSelect,
@@ -192,7 +261,8 @@ const RuleRow = ({ rule, onUpdate, onDelete, disabled }) => {
192
261
  value: rule.value,
193
262
  operator: rule.operator,
194
263
  onChange: (val) => onUpdate({ ...rule, value: val }),
195
- disabled
264
+ disabled,
265
+ inputColors: colors.input
196
266
  }
197
267
  ) }),
198
268
  /* @__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, {}) }) })
@@ -207,15 +277,8 @@ const RuleGroupComponent = ({
207
277
  depth = 0,
208
278
  disabled
209
279
  }) => {
210
- const theme = styledComponents.useTheme();
211
- const colors = theme?.colors;
212
- const borderColors = {
213
- 0: colors?.primary200 || "#d9d8ff",
214
- 1: colors?.success200 || "#c6f0c2",
215
- 2: colors?.warning200 || "#fae7b9",
216
- 3: colors?.danger200 || "#f5c0b8"
217
- };
218
- const borderColor = borderColors[depth % 4] || borderColors[0];
280
+ const colors = useThemeColors();
281
+ const borderColor = colors.depthBorders[depth % 4] || colors.depthBorders[0];
219
282
  const handleAddRule = () => {
220
283
  onUpdate(utils.addRuleToGroup(group, group.id, utils.createEmptyRule()));
221
284
  };
@@ -246,31 +309,88 @@ const RuleGroupComponent = ({
246
309
  return /* @__PURE__ */ jsxRuntime.jsxs(
247
310
  designSystem.Box,
248
311
  {
249
- padding: 3,
250
- background: depth === 0 ? "neutral100" : "neutral0",
312
+ padding: 4,
251
313
  hasRadius: true,
252
314
  style: {
315
+ background: depth === 0 ? colors.group.rootBg : colors.group.nestedBg,
253
316
  borderLeft: `3px solid ${borderColor}`,
254
317
  marginLeft: depth > 0 ? "12px" : 0
255
318
  },
256
319
  children: [
257
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, children: [
258
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
259
- /* @__PURE__ */ jsxRuntime.jsx(
260
- designSystem.Button,
261
- {
262
- variant: group.logic === "$and" ? "default" : "secondary",
263
- size: "S",
264
- onClick: handleLogicToggle,
265
- disabled,
266
- children: group.logic === "$and" ? "AND" : "OR"
267
- }
268
- ),
269
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: group.logic === "$and" ? "Все условия должны совпадать" : "Любое условие может совпадать" })
270
- ] }),
271
- 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, {}) }) })
272
- ] }),
273
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, children: group.rules.map((item, index) => {
320
+ /* @__PURE__ */ jsxRuntime.jsxs(
321
+ designSystem.Flex,
322
+ {
323
+ justifyContent: "space-between",
324
+ alignItems: "center",
325
+ marginBottom: 3,
326
+ padding: 2,
327
+ hasRadius: true,
328
+ style: { background: colors.group.headerBg },
329
+ children: [
330
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
331
+ /* @__PURE__ */ jsxRuntime.jsx(
332
+ designSystem.Button,
333
+ {
334
+ variant: group.logic === "$and" ? "default" : "secondary",
335
+ size: "S",
336
+ onClick: handleLogicToggle,
337
+ disabled,
338
+ children: group.logic === "$and" ? "AND" : "OR"
339
+ }
340
+ ),
341
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: group.logic === "$and" ? "Все условия должны совпадать" : "Любое условие может совпадать" })
342
+ ] }),
343
+ 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, {}) }) })
344
+ ]
345
+ }
346
+ ),
347
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, children: group.rules.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
348
+ designSystem.Box,
349
+ {
350
+ padding: 6,
351
+ hasRadius: true,
352
+ style: {
353
+ border: `2px dashed ${colors.emptyState.border}`,
354
+ textAlign: "center"
355
+ },
356
+ children: [
357
+ /* @__PURE__ */ jsxRuntime.jsx(
358
+ designSystem.Box,
359
+ {
360
+ style: {
361
+ width: "40px",
362
+ height: "40px",
363
+ borderRadius: "50%",
364
+ background: colors.emptyState.iconBg,
365
+ display: "inline-flex",
366
+ alignItems: "center",
367
+ justifyContent: "center",
368
+ marginBottom: "8px"
369
+ },
370
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Layout, {})
371
+ }
372
+ ),
373
+ /* @__PURE__ */ jsxRuntime.jsx(
374
+ designSystem.Typography,
375
+ {
376
+ variant: "omega",
377
+ textColor: "neutral500",
378
+ style: { display: "block", marginTop: 4 },
379
+ children: "Условий пока нет"
380
+ }
381
+ ),
382
+ /* @__PURE__ */ jsxRuntime.jsx(
383
+ designSystem.Typography,
384
+ {
385
+ variant: "pi",
386
+ textColor: "neutral400",
387
+ style: { display: "block", marginTop: 4 },
388
+ children: "Добавьте условие или группу для построения сегмента"
389
+ }
390
+ )
391
+ ]
392
+ }
393
+ ) : group.rules.map((item) => {
274
394
  if (utils.isRuleGroup(item)) {
275
395
  return /* @__PURE__ */ jsxRuntime.jsx(
276
396
  RuleGroupComponent,
@@ -329,6 +449,7 @@ const RuleGroupComponent = ({
329
449
  };
330
450
  const RulesBuilder = React.forwardRef(
331
451
  ({ name, value, onChange, intlLabel, disabled, error, required, hint }, ref) => {
452
+ const colors = useThemeColors();
332
453
  const [config, setConfig] = React.useState(() => utils.deserializeConfig(value));
333
454
  React.useEffect(() => {
334
455
  setConfig(utils.deserializeConfig(value));
@@ -354,11 +475,20 @@ const RulesBuilder = React.forwardRef(
354
475
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
355
476
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: displayLabel }),
356
477
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
357
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "neutral100", textColor: "neutral600", children: [
358
- rulesCount,
359
- " rule",
360
- rulesCount !== 1 ? "s" : ""
361
- ] }),
478
+ /* @__PURE__ */ jsxRuntime.jsxs(
479
+ designSystem.Badge,
480
+ {
481
+ style: {
482
+ backgroundColor: colors.isDark ? "#2d2d4a" : "#f0f0ff",
483
+ color: colors.isDark ? "#a5a5ba" : "#666687"
484
+ },
485
+ children: [
486
+ rulesCount,
487
+ " rule",
488
+ rulesCount !== 1 ? "s" : ""
489
+ ]
490
+ }
491
+ ),
362
492
  rulesCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "ghost", size: "S", onClick: handleClear, disabled, children: "Очистить всё" })
363
493
  ] })
364
494
  ] }),
@@ -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-By7yGs6Y.js"
38
+ "../_chunks/index-D4yBB_T5.js"
39
39
  ))
40
40
  },
41
41
  options: {
@@ -34,7 +34,7 @@ const index = {
34
34
  components: {
35
35
  Input: async () => import(
36
36
  /* webpackChunkName: "crm-rules-builder" */
37
- "../_chunks/index-OrZRTEP0.mjs"
37
+ "../_chunks/index-BMK03t54.mjs"
38
38
  )
39
39
  },
40
40
  options: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inspirer-dev/crm-dashboard",
3
- "version": "1.0.74",
3
+ "version": "1.0.76",
4
4
  "description": "CRM Dashboard and Tools",
5
5
  "strapi": {
6
6
  "name": "crm-dashboard",