@inspirer-dev/crm-dashboard 1.0.83 → 1.0.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/src/components/ButtonsBuilder/index.tsx +94 -58
- package/admin/src/components/TriggerParamsField/index.tsx +9 -14
- package/admin/src/types/crm.ts +3 -1
- package/dist/_chunks/{index-2rErXqfH.js → index-CqRPcaXV.js} +7 -11
- package/dist/_chunks/{index-BKfFI_Jo.mjs → index-DRhcN7TG.mjs} +72 -50
- package/dist/_chunks/{index-BMvCnlEy.mjs → index-DnyoVNLo.mjs} +8 -12
- package/dist/_chunks/{index-Cg0G_bTE.js → index-u-jR-zfs.js} +70 -48
- package/dist/admin/index.js +2 -2
- package/dist/admin/index.mjs +2 -2
- package/package.json +1 -1
|
@@ -10,16 +10,20 @@ import {
|
|
|
10
10
|
Tooltip,
|
|
11
11
|
Card,
|
|
12
12
|
CardContent,
|
|
13
|
+
SingleSelect,
|
|
14
|
+
SingleSelectOption,
|
|
13
15
|
} from '@strapi/design-system';
|
|
14
|
-
import { Plus, Trash, ArrowUp, ArrowDown, Link as LinkIcon } from '@strapi/icons';
|
|
16
|
+
import { Plus, Trash, ArrowUp, ArrowDown, Link as LinkIcon, Layout } from '@strapi/icons';
|
|
15
17
|
import { useTheme } from 'styled-components';
|
|
16
18
|
import { generateId } from '../RulesBuilder/utils';
|
|
17
19
|
|
|
18
20
|
type TelegramButton = {
|
|
19
21
|
id: string;
|
|
20
22
|
text: string;
|
|
21
|
-
url
|
|
23
|
+
url?: string;
|
|
22
24
|
row?: number;
|
|
25
|
+
type?: 'url' | 'screen';
|
|
26
|
+
screenSlug?: string;
|
|
23
27
|
};
|
|
24
28
|
|
|
25
29
|
interface ButtonsBuilderProps {
|
|
@@ -37,26 +41,25 @@ interface ButtonsBuilderProps {
|
|
|
37
41
|
hint?: string;
|
|
38
42
|
}
|
|
39
43
|
|
|
44
|
+
const mapRawButton = (b: any): TelegramButton => ({
|
|
45
|
+
id: typeof b?.id === 'string' ? b.id : generateId(),
|
|
46
|
+
text: typeof b?.text === 'string' ? b.text : '',
|
|
47
|
+
url: typeof b?.url === 'string' ? b.url : '',
|
|
48
|
+
row: typeof b?.row === 'number' ? b.row : 0,
|
|
49
|
+
type: b?.type === 'screen' ? 'screen' : 'url',
|
|
50
|
+
screenSlug: typeof b?.screenSlug === 'string' ? b.screenSlug : '',
|
|
51
|
+
});
|
|
52
|
+
|
|
40
53
|
const parseButtons = (value: string | TelegramButton[] | null | undefined): TelegramButton[] => {
|
|
41
54
|
if (!value) return [];
|
|
42
55
|
if (Array.isArray(value)) {
|
|
43
|
-
return value.map(
|
|
44
|
-
id: typeof b?.id === 'string' ? b.id : generateId(),
|
|
45
|
-
text: typeof b?.text === 'string' ? b.text : '',
|
|
46
|
-
url: typeof b?.url === 'string' ? b.url : '',
|
|
47
|
-
row: typeof b?.row === 'number' ? b.row : 0,
|
|
48
|
-
}));
|
|
56
|
+
return value.map(mapRawButton);
|
|
49
57
|
}
|
|
50
58
|
if (typeof value === 'string') {
|
|
51
59
|
try {
|
|
52
60
|
const parsed = JSON.parse(value);
|
|
53
61
|
if (!Array.isArray(parsed)) return [];
|
|
54
|
-
return parsed.map(
|
|
55
|
-
id: typeof b?.id === 'string' ? b.id : generateId(),
|
|
56
|
-
text: typeof b?.text === 'string' ? b.text : '',
|
|
57
|
-
url: typeof b?.url === 'string' ? b.url : '',
|
|
58
|
-
row: typeof b?.row === 'number' ? b.row : 0,
|
|
59
|
-
}));
|
|
62
|
+
return parsed.map(mapRawButton);
|
|
60
63
|
} catch {
|
|
61
64
|
return [];
|
|
62
65
|
}
|
|
@@ -103,35 +106,39 @@ const useThemeColors = () => {
|
|
|
103
106
|
}), [isDark]);
|
|
104
107
|
};
|
|
105
108
|
|
|
106
|
-
const TelegramButtonPreview: React.FC<{ text: string; url
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
109
|
+
const TelegramButtonPreview: React.FC<{ text: string; btnType?: 'url' | 'screen' }> = ({ text, btnType }) => {
|
|
110
|
+
const isScreen = btnType === 'screen';
|
|
111
|
+
const bgColor = isScreen ? '#7c3aed' : '#3390ec';
|
|
112
|
+
const bgHover = isScreen ? '#6d28d9' : '#2b7ed8';
|
|
113
|
+
return (
|
|
114
|
+
<span
|
|
115
|
+
style={{
|
|
116
|
+
display: 'inline-flex',
|
|
117
|
+
alignItems: 'center',
|
|
118
|
+
justifyContent: 'center',
|
|
119
|
+
gap: '6px',
|
|
120
|
+
padding: '8px 16px',
|
|
121
|
+
backgroundColor: bgColor,
|
|
122
|
+
color: '#ffffff',
|
|
123
|
+
borderRadius: '8px',
|
|
124
|
+
textDecoration: 'none',
|
|
125
|
+
fontSize: '14px',
|
|
126
|
+
fontWeight: 500,
|
|
127
|
+
cursor: 'default',
|
|
128
|
+
transition: 'background-color 0.15s ease',
|
|
129
|
+
minWidth: '80px',
|
|
130
|
+
}}
|
|
131
|
+
onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = bgHover)}
|
|
132
|
+
onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = bgColor)}
|
|
133
|
+
>
|
|
134
|
+
{isScreen
|
|
135
|
+
? <Layout width={14} height={14} style={{ opacity: 0.9 }} />
|
|
136
|
+
: <LinkIcon width={14} height={14} style={{ opacity: 0.9 }} />
|
|
137
|
+
}
|
|
138
|
+
<span>{text || '(empty)'}</span>
|
|
139
|
+
</span>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
135
142
|
|
|
136
143
|
const ButtonsBuilder = forwardRef<HTMLDivElement, ButtonsBuilderProps>(
|
|
137
144
|
({ name, value, onChange, intlLabel, disabled, error, required, hint }, ref) => {
|
|
@@ -150,7 +157,7 @@ const ButtonsBuilder = forwardRef<HTMLDivElement, ButtonsBuilderProps>(
|
|
|
150
157
|
const addButton = () => {
|
|
151
158
|
update([
|
|
152
159
|
...buttons,
|
|
153
|
-
{ id: generateId(), text: 'Button', url: 'https://cases.gg', row: 0 },
|
|
160
|
+
{ id: generateId(), text: 'Button', url: 'https://cases.gg', row: 0, type: 'url', screenSlug: '' },
|
|
154
161
|
]);
|
|
155
162
|
};
|
|
156
163
|
|
|
@@ -222,7 +229,8 @@ const ButtonsBuilder = forwardRef<HTMLDivElement, ButtonsBuilderProps>(
|
|
|
222
229
|
) : (
|
|
223
230
|
<Flex direction="column" gap={2}>
|
|
224
231
|
{buttons.map((btn, idx) => {
|
|
225
|
-
const
|
|
232
|
+
const isScreen = btn.type === 'screen';
|
|
233
|
+
const urlOk = isScreen || (btn.url ?? '').length === 0 || isValidUrl(btn.url ?? '');
|
|
226
234
|
return (
|
|
227
235
|
<Card
|
|
228
236
|
key={btn.id}
|
|
@@ -251,31 +259,59 @@ const ButtonsBuilder = forwardRef<HTMLDivElement, ButtonsBuilderProps>(
|
|
|
251
259
|
</Typography>
|
|
252
260
|
</Box>
|
|
253
261
|
|
|
254
|
-
<Box style={{
|
|
255
|
-
<
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
updateButton(btn.id, { text: e.target.value })
|
|
262
|
+
<Box style={{ width: 100, flexShrink: 0 }}>
|
|
263
|
+
<SingleSelect
|
|
264
|
+
value={btn.type || 'url'}
|
|
265
|
+
onChange={(val: string) =>
|
|
266
|
+
updateButton(btn.id, { type: val as 'url' | 'screen' })
|
|
260
267
|
}
|
|
261
268
|
disabled={disabled}
|
|
262
269
|
size="S"
|
|
263
|
-
|
|
270
|
+
>
|
|
271
|
+
<SingleSelectOption value="url">URL</SingleSelectOption>
|
|
272
|
+
<SingleSelectOption value="screen">Screen</SingleSelectOption>
|
|
273
|
+
</SingleSelect>
|
|
264
274
|
</Box>
|
|
265
275
|
|
|
266
|
-
<Box style={{ flex:
|
|
276
|
+
<Box style={{ flex: 1, minWidth: 120 }}>
|
|
267
277
|
<TextInput
|
|
268
|
-
placeholder="
|
|
269
|
-
value={btn.
|
|
278
|
+
placeholder="Button text"
|
|
279
|
+
value={btn.text}
|
|
270
280
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
|
271
|
-
updateButton(btn.id, {
|
|
281
|
+
updateButton(btn.id, { text: e.target.value })
|
|
272
282
|
}
|
|
273
283
|
disabled={disabled}
|
|
274
284
|
size="S"
|
|
275
|
-
hasError={!urlOk}
|
|
276
285
|
/>
|
|
277
286
|
</Box>
|
|
278
287
|
|
|
288
|
+
{isScreen ? (
|
|
289
|
+
<Box style={{ flex: 2, minWidth: 180 }}>
|
|
290
|
+
<TextInput
|
|
291
|
+
placeholder="screen-slug"
|
|
292
|
+
value={btn.screenSlug ?? ''}
|
|
293
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
|
294
|
+
updateButton(btn.id, { screenSlug: e.target.value })
|
|
295
|
+
}
|
|
296
|
+
disabled={disabled}
|
|
297
|
+
size="S"
|
|
298
|
+
/>
|
|
299
|
+
</Box>
|
|
300
|
+
) : (
|
|
301
|
+
<Box style={{ flex: 2, minWidth: 180 }}>
|
|
302
|
+
<TextInput
|
|
303
|
+
placeholder="https://..."
|
|
304
|
+
value={btn.url ?? ''}
|
|
305
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
|
306
|
+
updateButton(btn.id, { url: e.target.value })
|
|
307
|
+
}
|
|
308
|
+
disabled={disabled}
|
|
309
|
+
size="S"
|
|
310
|
+
hasError={!urlOk}
|
|
311
|
+
/>
|
|
312
|
+
</Box>
|
|
313
|
+
)}
|
|
314
|
+
|
|
279
315
|
<Box style={{ width: 60 }}>
|
|
280
316
|
<TextInput
|
|
281
317
|
type="number"
|
|
@@ -359,7 +395,7 @@ const ButtonsBuilder = forwardRef<HTMLDivElement, ButtonsBuilderProps>(
|
|
|
359
395
|
{previewRows.map((row) => (
|
|
360
396
|
<Flex key={row.row} gap={2} wrap="wrap" justifyContent="center">
|
|
361
397
|
{row.items.map((b) => (
|
|
362
|
-
<TelegramButtonPreview key={b.id} text={b.text}
|
|
398
|
+
<TelegramButtonPreview key={b.id} text={b.text} btnType={b.type} />
|
|
363
399
|
))}
|
|
364
400
|
</Flex>
|
|
365
401
|
))}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { forwardRef,
|
|
1
|
+
import React, { forwardRef, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Box,
|
|
4
4
|
Button,
|
|
@@ -17,7 +17,7 @@ import { Plus, Trash } from '@strapi/icons';
|
|
|
17
17
|
import { useTheme } from 'styled-components';
|
|
18
18
|
import { TRIGGER_PARAMS, STAGE_TYPE_LABELS, type TriggerParamDef } from './constants';
|
|
19
19
|
|
|
20
|
-
type TriggerParamsValue = Record<string, number |
|
|
20
|
+
type TriggerParamsValue = Record<string, number | null>;
|
|
21
21
|
|
|
22
22
|
interface TriggerParamsFieldProps {
|
|
23
23
|
name: string;
|
|
@@ -47,13 +47,11 @@ const parseValue = (value: string | TriggerParamsValue | null | undefined): Trig
|
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
const serialize = (params: TriggerParamsValue): string => {
|
|
50
|
-
const
|
|
50
|
+
const out: Record<string, number> = {};
|
|
51
51
|
for (const [key, val] of Object.entries(params)) {
|
|
52
|
-
if (val
|
|
53
|
-
clean[key] = val;
|
|
54
|
-
}
|
|
52
|
+
if (typeof val === 'number') out[key] = val;
|
|
55
53
|
}
|
|
56
|
-
return JSON.stringify(
|
|
54
|
+
return JSON.stringify(out);
|
|
57
55
|
};
|
|
58
56
|
|
|
59
57
|
interface StrapiTheme {
|
|
@@ -77,13 +75,10 @@ const useThemeColors = () => {
|
|
|
77
75
|
|
|
78
76
|
const TriggerParamsField = forwardRef<HTMLDivElement, TriggerParamsFieldProps>(
|
|
79
77
|
({ name, value, onChange, intlLabel, disabled, error, required, hint }, ref) => {
|
|
80
|
-
const
|
|
78
|
+
const initialRef = useRef(value);
|
|
79
|
+
const [params, setParams] = useState<TriggerParamsValue>(() => parseValue(initialRef.current));
|
|
81
80
|
const colors = useThemeColors();
|
|
82
81
|
|
|
83
|
-
useEffect(() => {
|
|
84
|
-
setParams(parseValue(value));
|
|
85
|
-
}, [value]);
|
|
86
|
-
|
|
87
82
|
const update = (next: TriggerParamsValue) => {
|
|
88
83
|
setParams(next);
|
|
89
84
|
onChange({ target: { name, value: serialize(next) } });
|
|
@@ -96,7 +91,7 @@ const TriggerParamsField = forwardRef<HTMLDivElement, TriggerParamsFieldProps>(
|
|
|
96
91
|
const addParam = (key: string) => {
|
|
97
92
|
const def = TRIGGER_PARAMS.find((p) => p.key === key);
|
|
98
93
|
if (!def) return;
|
|
99
|
-
update({ ...params, [key]:
|
|
94
|
+
update({ ...params, [key]: null });
|
|
100
95
|
};
|
|
101
96
|
|
|
102
97
|
const removeParam = (key: string) => {
|
|
@@ -106,7 +101,7 @@ const TriggerParamsField = forwardRef<HTMLDivElement, TriggerParamsFieldProps>(
|
|
|
106
101
|
};
|
|
107
102
|
|
|
108
103
|
const setParamValue = (key: string, val: number | undefined) => {
|
|
109
|
-
update({ ...params, [key]: val });
|
|
104
|
+
update({ ...params, [key]: val ?? null });
|
|
110
105
|
};
|
|
111
106
|
|
|
112
107
|
const getParamDef = (key: string): TriggerParamDef | undefined =>
|
package/admin/src/types/crm.ts
CHANGED
|
@@ -116,13 +116,11 @@ const parseValue = (value) => {
|
|
|
116
116
|
return {};
|
|
117
117
|
};
|
|
118
118
|
const serialize = (params) => {
|
|
119
|
-
const
|
|
119
|
+
const out = {};
|
|
120
120
|
for (const [key, val] of Object.entries(params)) {
|
|
121
|
-
if (val
|
|
122
|
-
clean[key] = val;
|
|
123
|
-
}
|
|
121
|
+
if (typeof val === "number") out[key] = val;
|
|
124
122
|
}
|
|
125
|
-
return JSON.stringify(
|
|
123
|
+
return JSON.stringify(out);
|
|
126
124
|
};
|
|
127
125
|
const useThemeColors = () => {
|
|
128
126
|
const theme = styledComponents.useTheme();
|
|
@@ -139,11 +137,9 @@ const useThemeColors = () => {
|
|
|
139
137
|
};
|
|
140
138
|
const TriggerParamsField = React.forwardRef(
|
|
141
139
|
({ name, value, onChange, intlLabel, disabled, error, required, hint }, ref) => {
|
|
142
|
-
const
|
|
140
|
+
const initialRef = React.useRef(value);
|
|
141
|
+
const [params, setParams] = React.useState(() => parseValue(initialRef.current));
|
|
143
142
|
const colors = useThemeColors();
|
|
144
|
-
React.useEffect(() => {
|
|
145
|
-
setParams(parseValue(value));
|
|
146
|
-
}, [value]);
|
|
147
143
|
const update = (next) => {
|
|
148
144
|
setParams(next);
|
|
149
145
|
onChange({ target: { name, value: serialize(next) } });
|
|
@@ -153,7 +149,7 @@ const TriggerParamsField = React.forwardRef(
|
|
|
153
149
|
const addParam = (key) => {
|
|
154
150
|
const def = TRIGGER_PARAMS.find((p) => p.key === key);
|
|
155
151
|
if (!def) return;
|
|
156
|
-
update({ ...params, [key]:
|
|
152
|
+
update({ ...params, [key]: null });
|
|
157
153
|
};
|
|
158
154
|
const removeParam = (key) => {
|
|
159
155
|
const next = { ...params };
|
|
@@ -161,7 +157,7 @@ const TriggerParamsField = React.forwardRef(
|
|
|
161
157
|
update(next);
|
|
162
158
|
};
|
|
163
159
|
const setParamValue = (key, val) => {
|
|
164
|
-
update({ ...params, [key]: val });
|
|
160
|
+
update({ ...params, [key]: val ?? null });
|
|
165
161
|
};
|
|
166
162
|
const getParamDef = (key) => TRIGGER_PARAMS.find((p) => p.key === key);
|
|
167
163
|
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Root, { name, error, required, hint, ref, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 3, children: [
|
|
@@ -1,29 +1,27 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef, useState, useEffect, useMemo } from "react";
|
|
3
|
-
import { Field, Flex, Box, Typography, Button, Card, CardContent, TextInput, Tooltip, IconButton } from "@strapi/design-system";
|
|
4
|
-
import { Plus, ArrowUp, ArrowDown, Trash, Link } from "@strapi/icons";
|
|
3
|
+
import { Field, Flex, Box, Typography, Button, Card, CardContent, SingleSelect, SingleSelectOption, TextInput, Tooltip, IconButton } from "@strapi/design-system";
|
|
4
|
+
import { Plus, ArrowUp, ArrowDown, Trash, Layout, Link } from "@strapi/icons";
|
|
5
5
|
import { useTheme } from "styled-components";
|
|
6
6
|
import { m as generateId } from "./utils-BHneBJ7U.mjs";
|
|
7
|
+
const mapRawButton = (b) => ({
|
|
8
|
+
id: typeof b?.id === "string" ? b.id : generateId(),
|
|
9
|
+
text: typeof b?.text === "string" ? b.text : "",
|
|
10
|
+
url: typeof b?.url === "string" ? b.url : "",
|
|
11
|
+
row: typeof b?.row === "number" ? b.row : 0,
|
|
12
|
+
type: b?.type === "screen" ? "screen" : "url",
|
|
13
|
+
screenSlug: typeof b?.screenSlug === "string" ? b.screenSlug : ""
|
|
14
|
+
});
|
|
7
15
|
const parseButtons = (value) => {
|
|
8
16
|
if (!value) return [];
|
|
9
17
|
if (Array.isArray(value)) {
|
|
10
|
-
return value.map(
|
|
11
|
-
id: typeof b?.id === "string" ? b.id : generateId(),
|
|
12
|
-
text: typeof b?.text === "string" ? b.text : "",
|
|
13
|
-
url: typeof b?.url === "string" ? b.url : "",
|
|
14
|
-
row: typeof b?.row === "number" ? b.row : 0
|
|
15
|
-
}));
|
|
18
|
+
return value.map(mapRawButton);
|
|
16
19
|
}
|
|
17
20
|
if (typeof value === "string") {
|
|
18
21
|
try {
|
|
19
22
|
const parsed = JSON.parse(value);
|
|
20
23
|
if (!Array.isArray(parsed)) return [];
|
|
21
|
-
return parsed.map(
|
|
22
|
-
id: typeof b?.id === "string" ? b.id : generateId(),
|
|
23
|
-
text: typeof b?.text === "string" ? b.text : "",
|
|
24
|
-
url: typeof b?.url === "string" ? b.url : "",
|
|
25
|
-
row: typeof b?.row === "number" ? b.row : 0
|
|
26
|
-
}));
|
|
24
|
+
return parsed.map(mapRawButton);
|
|
27
25
|
} catch {
|
|
28
26
|
return [];
|
|
29
27
|
}
|
|
@@ -59,37 +57,38 @@ const useThemeColors = () => {
|
|
|
59
57
|
}
|
|
60
58
|
}), [isDark]);
|
|
61
59
|
};
|
|
62
|
-
const TelegramButtonPreview = ({ text,
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
);
|
|
60
|
+
const TelegramButtonPreview = ({ text, btnType }) => {
|
|
61
|
+
const isScreen = btnType === "screen";
|
|
62
|
+
const bgColor = isScreen ? "#7c3aed" : "#3390ec";
|
|
63
|
+
const bgHover = isScreen ? "#6d28d9" : "#2b7ed8";
|
|
64
|
+
return /* @__PURE__ */ jsxs(
|
|
65
|
+
"span",
|
|
66
|
+
{
|
|
67
|
+
style: {
|
|
68
|
+
display: "inline-flex",
|
|
69
|
+
alignItems: "center",
|
|
70
|
+
justifyContent: "center",
|
|
71
|
+
gap: "6px",
|
|
72
|
+
padding: "8px 16px",
|
|
73
|
+
backgroundColor: bgColor,
|
|
74
|
+
color: "#ffffff",
|
|
75
|
+
borderRadius: "8px",
|
|
76
|
+
textDecoration: "none",
|
|
77
|
+
fontSize: "14px",
|
|
78
|
+
fontWeight: 500,
|
|
79
|
+
cursor: "default",
|
|
80
|
+
transition: "background-color 0.15s ease",
|
|
81
|
+
minWidth: "80px"
|
|
82
|
+
},
|
|
83
|
+
onMouseEnter: (e) => e.currentTarget.style.backgroundColor = bgHover,
|
|
84
|
+
onMouseLeave: (e) => e.currentTarget.style.backgroundColor = bgColor,
|
|
85
|
+
children: [
|
|
86
|
+
isScreen ? /* @__PURE__ */ jsx(Layout, { width: 14, height: 14, style: { opacity: 0.9 } }) : /* @__PURE__ */ jsx(Link, { width: 14, height: 14, style: { opacity: 0.9 } }),
|
|
87
|
+
/* @__PURE__ */ jsx("span", { children: text || "(empty)" })
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
};
|
|
93
92
|
const ButtonsBuilder = forwardRef(
|
|
94
93
|
({ name, value, onChange, intlLabel, disabled, error, required, hint }, ref) => {
|
|
95
94
|
const [buttons, setButtons] = useState(() => parseButtons(value));
|
|
@@ -104,7 +103,7 @@ const ButtonsBuilder = forwardRef(
|
|
|
104
103
|
const addButton = () => {
|
|
105
104
|
update([
|
|
106
105
|
...buttons,
|
|
107
|
-
{ id: generateId(), text: "Button", url: "https://cases.gg", row: 0 }
|
|
106
|
+
{ id: generateId(), text: "Button", url: "https://cases.gg", row: 0, type: "url", screenSlug: "" }
|
|
108
107
|
]);
|
|
109
108
|
};
|
|
110
109
|
const updateButton = (id, patch) => {
|
|
@@ -162,7 +161,8 @@ const ButtonsBuilder = forwardRef(
|
|
|
162
161
|
]
|
|
163
162
|
}
|
|
164
163
|
) : /* @__PURE__ */ jsx(Flex, { direction: "column", gap: 2, children: buttons.map((btn, idx) => {
|
|
165
|
-
const
|
|
164
|
+
const isScreen = btn.type === "screen";
|
|
165
|
+
const urlOk = isScreen || (btn.url ?? "").length === 0 || isValidUrl(btn.url ?? "");
|
|
166
166
|
return /* @__PURE__ */ jsx(
|
|
167
167
|
Card,
|
|
168
168
|
{
|
|
@@ -189,6 +189,19 @@ const ButtonsBuilder = forwardRef(
|
|
|
189
189
|
children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "primary600", children: idx + 1 })
|
|
190
190
|
}
|
|
191
191
|
),
|
|
192
|
+
/* @__PURE__ */ jsx(Box, { style: { width: 100, flexShrink: 0 }, children: /* @__PURE__ */ jsxs(
|
|
193
|
+
SingleSelect,
|
|
194
|
+
{
|
|
195
|
+
value: btn.type || "url",
|
|
196
|
+
onChange: (val) => updateButton(btn.id, { type: val }),
|
|
197
|
+
disabled,
|
|
198
|
+
size: "S",
|
|
199
|
+
children: [
|
|
200
|
+
/* @__PURE__ */ jsx(SingleSelectOption, { value: "url", children: "URL" }),
|
|
201
|
+
/* @__PURE__ */ jsx(SingleSelectOption, { value: "screen", children: "Screen" })
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
) }),
|
|
192
205
|
/* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 120 }, children: /* @__PURE__ */ jsx(
|
|
193
206
|
TextInput,
|
|
194
207
|
{
|
|
@@ -199,11 +212,20 @@ const ButtonsBuilder = forwardRef(
|
|
|
199
212
|
size: "S"
|
|
200
213
|
}
|
|
201
214
|
) }),
|
|
202
|
-
/* @__PURE__ */ jsx(Box, { style: { flex: 2, minWidth: 180 }, children: /* @__PURE__ */ jsx(
|
|
215
|
+
isScreen ? /* @__PURE__ */ jsx(Box, { style: { flex: 2, minWidth: 180 }, children: /* @__PURE__ */ jsx(
|
|
216
|
+
TextInput,
|
|
217
|
+
{
|
|
218
|
+
placeholder: "screen-slug",
|
|
219
|
+
value: btn.screenSlug ?? "",
|
|
220
|
+
onChange: (e) => updateButton(btn.id, { screenSlug: e.target.value }),
|
|
221
|
+
disabled,
|
|
222
|
+
size: "S"
|
|
223
|
+
}
|
|
224
|
+
) }) : /* @__PURE__ */ jsx(Box, { style: { flex: 2, minWidth: 180 }, children: /* @__PURE__ */ jsx(
|
|
203
225
|
TextInput,
|
|
204
226
|
{
|
|
205
227
|
placeholder: "https://...",
|
|
206
|
-
value: btn.url,
|
|
228
|
+
value: btn.url ?? "",
|
|
207
229
|
onChange: (e) => updateButton(btn.id, { url: e.target.value }),
|
|
208
230
|
disabled,
|
|
209
231
|
size: "S",
|
|
@@ -282,7 +304,7 @@ const ButtonsBuilder = forwardRef(
|
|
|
282
304
|
children: "PREVIEW"
|
|
283
305
|
}
|
|
284
306
|
),
|
|
285
|
-
/* @__PURE__ */ jsx(Flex, { direction: "column", gap: 2, alignItems: "center", children: previewRows.map((row) => /* @__PURE__ */ jsx(Flex, { gap: 2, wrap: "wrap", justifyContent: "center", children: row.items.map((b) => /* @__PURE__ */ jsx(TelegramButtonPreview, { text: b.text,
|
|
307
|
+
/* @__PURE__ */ jsx(Flex, { direction: "column", gap: 2, alignItems: "center", children: previewRows.map((row) => /* @__PURE__ */ jsx(Flex, { gap: 2, wrap: "wrap", justifyContent: "center", children: row.items.map((b) => /* @__PURE__ */ jsx(TelegramButtonPreview, { text: b.text, btnType: b.type }, b.id)) }, row.row)) })
|
|
286
308
|
]
|
|
287
309
|
}
|
|
288
310
|
),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef,
|
|
2
|
+
import { forwardRef, useRef, useState, useMemo } from "react";
|
|
3
3
|
import { Field, Flex, Box, Typography, Card, CardContent, NumberInput, Tooltip, IconButton, SingleSelect, SingleSelectOption, Button } from "@strapi/design-system";
|
|
4
4
|
import { Trash, Plus } from "@strapi/icons";
|
|
5
5
|
import { useTheme } from "styled-components";
|
|
@@ -114,13 +114,11 @@ const parseValue = (value) => {
|
|
|
114
114
|
return {};
|
|
115
115
|
};
|
|
116
116
|
const serialize = (params) => {
|
|
117
|
-
const
|
|
117
|
+
const out = {};
|
|
118
118
|
for (const [key, val] of Object.entries(params)) {
|
|
119
|
-
if (val
|
|
120
|
-
clean[key] = val;
|
|
121
|
-
}
|
|
119
|
+
if (typeof val === "number") out[key] = val;
|
|
122
120
|
}
|
|
123
|
-
return JSON.stringify(
|
|
121
|
+
return JSON.stringify(out);
|
|
124
122
|
};
|
|
125
123
|
const useThemeColors = () => {
|
|
126
124
|
const theme = useTheme();
|
|
@@ -137,11 +135,9 @@ const useThemeColors = () => {
|
|
|
137
135
|
};
|
|
138
136
|
const TriggerParamsField = forwardRef(
|
|
139
137
|
({ name, value, onChange, intlLabel, disabled, error, required, hint }, ref) => {
|
|
140
|
-
const
|
|
138
|
+
const initialRef = useRef(value);
|
|
139
|
+
const [params, setParams] = useState(() => parseValue(initialRef.current));
|
|
141
140
|
const colors = useThemeColors();
|
|
142
|
-
useEffect(() => {
|
|
143
|
-
setParams(parseValue(value));
|
|
144
|
-
}, [value]);
|
|
145
141
|
const update = (next) => {
|
|
146
142
|
setParams(next);
|
|
147
143
|
onChange({ target: { name, value: serialize(next) } });
|
|
@@ -151,7 +147,7 @@ const TriggerParamsField = forwardRef(
|
|
|
151
147
|
const addParam = (key) => {
|
|
152
148
|
const def = TRIGGER_PARAMS.find((p) => p.key === key);
|
|
153
149
|
if (!def) return;
|
|
154
|
-
update({ ...params, [key]:
|
|
150
|
+
update({ ...params, [key]: null });
|
|
155
151
|
};
|
|
156
152
|
const removeParam = (key) => {
|
|
157
153
|
const next = { ...params };
|
|
@@ -159,7 +155,7 @@ const TriggerParamsField = forwardRef(
|
|
|
159
155
|
update(next);
|
|
160
156
|
};
|
|
161
157
|
const setParamValue = (key, val) => {
|
|
162
|
-
update({ ...params, [key]: val });
|
|
158
|
+
update({ ...params, [key]: val ?? null });
|
|
163
159
|
};
|
|
164
160
|
const getParamDef = (key) => TRIGGER_PARAMS.find((p) => p.key === key);
|
|
165
161
|
return /* @__PURE__ */ jsx(Field.Root, { name, error, required, hint, ref, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
|
|
@@ -6,26 +6,24 @@ const designSystem = require("@strapi/design-system");
|
|
|
6
6
|
const icons = require("@strapi/icons");
|
|
7
7
|
const styledComponents = require("styled-components");
|
|
8
8
|
const utils = require("./utils-CAs_GSGv.js");
|
|
9
|
+
const mapRawButton = (b) => ({
|
|
10
|
+
id: typeof b?.id === "string" ? b.id : utils.generateId(),
|
|
11
|
+
text: typeof b?.text === "string" ? b.text : "",
|
|
12
|
+
url: typeof b?.url === "string" ? b.url : "",
|
|
13
|
+
row: typeof b?.row === "number" ? b.row : 0,
|
|
14
|
+
type: b?.type === "screen" ? "screen" : "url",
|
|
15
|
+
screenSlug: typeof b?.screenSlug === "string" ? b.screenSlug : ""
|
|
16
|
+
});
|
|
9
17
|
const parseButtons = (value) => {
|
|
10
18
|
if (!value) return [];
|
|
11
19
|
if (Array.isArray(value)) {
|
|
12
|
-
return value.map(
|
|
13
|
-
id: typeof b?.id === "string" ? b.id : utils.generateId(),
|
|
14
|
-
text: typeof b?.text === "string" ? b.text : "",
|
|
15
|
-
url: typeof b?.url === "string" ? b.url : "",
|
|
16
|
-
row: typeof b?.row === "number" ? b.row : 0
|
|
17
|
-
}));
|
|
20
|
+
return value.map(mapRawButton);
|
|
18
21
|
}
|
|
19
22
|
if (typeof value === "string") {
|
|
20
23
|
try {
|
|
21
24
|
const parsed = JSON.parse(value);
|
|
22
25
|
if (!Array.isArray(parsed)) return [];
|
|
23
|
-
return parsed.map(
|
|
24
|
-
id: typeof b?.id === "string" ? b.id : utils.generateId(),
|
|
25
|
-
text: typeof b?.text === "string" ? b.text : "",
|
|
26
|
-
url: typeof b?.url === "string" ? b.url : "",
|
|
27
|
-
row: typeof b?.row === "number" ? b.row : 0
|
|
28
|
-
}));
|
|
26
|
+
return parsed.map(mapRawButton);
|
|
29
27
|
} catch {
|
|
30
28
|
return [];
|
|
31
29
|
}
|
|
@@ -61,37 +59,38 @@ const useThemeColors = () => {
|
|
|
61
59
|
}
|
|
62
60
|
}), [isDark]);
|
|
63
61
|
};
|
|
64
|
-
const TelegramButtonPreview = ({ text,
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
);
|
|
62
|
+
const TelegramButtonPreview = ({ text, btnType }) => {
|
|
63
|
+
const isScreen = btnType === "screen";
|
|
64
|
+
const bgColor = isScreen ? "#7c3aed" : "#3390ec";
|
|
65
|
+
const bgHover = isScreen ? "#6d28d9" : "#2b7ed8";
|
|
66
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
67
|
+
"span",
|
|
68
|
+
{
|
|
69
|
+
style: {
|
|
70
|
+
display: "inline-flex",
|
|
71
|
+
alignItems: "center",
|
|
72
|
+
justifyContent: "center",
|
|
73
|
+
gap: "6px",
|
|
74
|
+
padding: "8px 16px",
|
|
75
|
+
backgroundColor: bgColor,
|
|
76
|
+
color: "#ffffff",
|
|
77
|
+
borderRadius: "8px",
|
|
78
|
+
textDecoration: "none",
|
|
79
|
+
fontSize: "14px",
|
|
80
|
+
fontWeight: 500,
|
|
81
|
+
cursor: "default",
|
|
82
|
+
transition: "background-color 0.15s ease",
|
|
83
|
+
minWidth: "80px"
|
|
84
|
+
},
|
|
85
|
+
onMouseEnter: (e) => e.currentTarget.style.backgroundColor = bgHover,
|
|
86
|
+
onMouseLeave: (e) => e.currentTarget.style.backgroundColor = bgColor,
|
|
87
|
+
children: [
|
|
88
|
+
isScreen ? /* @__PURE__ */ jsxRuntime.jsx(icons.Layout, { width: 14, height: 14, style: { opacity: 0.9 } }) : /* @__PURE__ */ jsxRuntime.jsx(icons.Link, { width: 14, height: 14, style: { opacity: 0.9 } }),
|
|
89
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: text || "(empty)" })
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
};
|
|
95
94
|
const ButtonsBuilder = React.forwardRef(
|
|
96
95
|
({ name, value, onChange, intlLabel, disabled, error, required, hint }, ref) => {
|
|
97
96
|
const [buttons, setButtons] = React.useState(() => parseButtons(value));
|
|
@@ -106,7 +105,7 @@ const ButtonsBuilder = React.forwardRef(
|
|
|
106
105
|
const addButton = () => {
|
|
107
106
|
update([
|
|
108
107
|
...buttons,
|
|
109
|
-
{ id: utils.generateId(), text: "Button", url: "https://cases.gg", row: 0 }
|
|
108
|
+
{ id: utils.generateId(), text: "Button", url: "https://cases.gg", row: 0, type: "url", screenSlug: "" }
|
|
110
109
|
]);
|
|
111
110
|
};
|
|
112
111
|
const updateButton = (id, patch) => {
|
|
@@ -164,7 +163,8 @@ const ButtonsBuilder = React.forwardRef(
|
|
|
164
163
|
]
|
|
165
164
|
}
|
|
166
165
|
) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, children: buttons.map((btn, idx) => {
|
|
167
|
-
const
|
|
166
|
+
const isScreen = btn.type === "screen";
|
|
167
|
+
const urlOk = isScreen || (btn.url ?? "").length === 0 || isValidUrl(btn.url ?? "");
|
|
168
168
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
169
169
|
designSystem.Card,
|
|
170
170
|
{
|
|
@@ -191,6 +191,19 @@ const ButtonsBuilder = React.forwardRef(
|
|
|
191
191
|
children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "primary600", children: idx + 1 })
|
|
192
192
|
}
|
|
193
193
|
),
|
|
194
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 100, flexShrink: 0 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
195
|
+
designSystem.SingleSelect,
|
|
196
|
+
{
|
|
197
|
+
value: btn.type || "url",
|
|
198
|
+
onChange: (val) => updateButton(btn.id, { type: val }),
|
|
199
|
+
disabled,
|
|
200
|
+
size: "S",
|
|
201
|
+
children: [
|
|
202
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "url", children: "URL" }),
|
|
203
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "screen", children: "Screen" })
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
) }),
|
|
194
207
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 120 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
195
208
|
designSystem.TextInput,
|
|
196
209
|
{
|
|
@@ -201,11 +214,20 @@ const ButtonsBuilder = React.forwardRef(
|
|
|
201
214
|
size: "S"
|
|
202
215
|
}
|
|
203
216
|
) }),
|
|
204
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 2, minWidth: 180 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
217
|
+
isScreen ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 2, minWidth: 180 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
218
|
+
designSystem.TextInput,
|
|
219
|
+
{
|
|
220
|
+
placeholder: "screen-slug",
|
|
221
|
+
value: btn.screenSlug ?? "",
|
|
222
|
+
onChange: (e) => updateButton(btn.id, { screenSlug: e.target.value }),
|
|
223
|
+
disabled,
|
|
224
|
+
size: "S"
|
|
225
|
+
}
|
|
226
|
+
) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 2, minWidth: 180 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
205
227
|
designSystem.TextInput,
|
|
206
228
|
{
|
|
207
229
|
placeholder: "https://...",
|
|
208
|
-
value: btn.url,
|
|
230
|
+
value: btn.url ?? "",
|
|
209
231
|
onChange: (e) => updateButton(btn.id, { url: e.target.value }),
|
|
210
232
|
disabled,
|
|
211
233
|
size: "S",
|
|
@@ -284,7 +306,7 @@ const ButtonsBuilder = React.forwardRef(
|
|
|
284
306
|
children: "PREVIEW"
|
|
285
307
|
}
|
|
286
308
|
),
|
|
287
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, alignItems: "center", children: previewRows.map((row) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, wrap: "wrap", justifyContent: "center", children: row.items.map((b) => /* @__PURE__ */ jsxRuntime.jsx(TelegramButtonPreview, { text: b.text,
|
|
309
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, alignItems: "center", children: previewRows.map((row) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, wrap: "wrap", justifyContent: "center", children: row.items.map((b) => /* @__PURE__ */ jsxRuntime.jsx(TelegramButtonPreview, { text: b.text, btnType: b.type }, b.id)) }, row.row)) })
|
|
288
310
|
]
|
|
289
311
|
}
|
|
290
312
|
),
|
package/dist/admin/index.js
CHANGED
|
@@ -107,7 +107,7 @@ const index = {
|
|
|
107
107
|
components: {
|
|
108
108
|
Input: async () => Promise.resolve().then(() => require(
|
|
109
109
|
/* webpackChunkName: "crm-telegram-buttons" */
|
|
110
|
-
"../_chunks/index-
|
|
110
|
+
"../_chunks/index-u-jR-zfs.js"
|
|
111
111
|
))
|
|
112
112
|
},
|
|
113
113
|
options: {
|
|
@@ -155,7 +155,7 @@ const index = {
|
|
|
155
155
|
components: {
|
|
156
156
|
Input: async () => Promise.resolve().then(() => require(
|
|
157
157
|
/* webpackChunkName: "crm-trigger-params" */
|
|
158
|
-
"../_chunks/index-
|
|
158
|
+
"../_chunks/index-CqRPcaXV.js"
|
|
159
159
|
))
|
|
160
160
|
},
|
|
161
161
|
options: {
|
package/dist/admin/index.mjs
CHANGED
|
@@ -106,7 +106,7 @@ const index = {
|
|
|
106
106
|
components: {
|
|
107
107
|
Input: async () => import(
|
|
108
108
|
/* webpackChunkName: "crm-telegram-buttons" */
|
|
109
|
-
"../_chunks/index-
|
|
109
|
+
"../_chunks/index-DRhcN7TG.mjs"
|
|
110
110
|
)
|
|
111
111
|
},
|
|
112
112
|
options: {
|
|
@@ -154,7 +154,7 @@ const index = {
|
|
|
154
154
|
components: {
|
|
155
155
|
Input: async () => import(
|
|
156
156
|
/* webpackChunkName: "crm-trigger-params" */
|
|
157
|
-
"../_chunks/index-
|
|
157
|
+
"../_chunks/index-DnyoVNLo.mjs"
|
|
158
158
|
)
|
|
159
159
|
},
|
|
160
160
|
options: {
|