@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,380 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const React = require("react");
5
+ const designSystem = require("@strapi/design-system");
6
+ const icons = require("@strapi/icons");
7
+ const styled = require("styled-components");
8
+ const utils = require("./utils-CmonL0io.js");
9
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
10
+ const React__default = /* @__PURE__ */ _interopDefault(React);
11
+ const ValueInput = ({ metric, value, operator, onChange, disabled }) => {
12
+ if (operator === "$exists") {
13
+ const boolVal = value === true || value === "true";
14
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
15
+ /* @__PURE__ */ jsxRuntime.jsx(
16
+ designSystem.Switch,
17
+ {
18
+ label: "",
19
+ selected: boolVal,
20
+ onChange: () => onChange(!boolVal),
21
+ disabled
22
+ }
23
+ ),
24
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", children: boolVal ? "exists" : "not exists" })
25
+ ] });
26
+ }
27
+ if (metric.valueType === "boolean") {
28
+ const boolVal = value === true || value === "true";
29
+ return /* @__PURE__ */ jsxRuntime.jsxs(
30
+ designSystem.SingleSelect,
31
+ {
32
+ value: boolVal ? "true" : "false",
33
+ onChange: (val) => onChange(val === "true"),
34
+ disabled,
35
+ size: "S",
36
+ children: [
37
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "true", children: "Yes" }),
38
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "false", children: "No" })
39
+ ]
40
+ }
41
+ );
42
+ }
43
+ if (metric.valueType === "time_ago") {
44
+ const { amount, unit } = utils.parseTimeAgoValue(value);
45
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
46
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "80px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
47
+ designSystem.TextInput,
48
+ {
49
+ type: "number",
50
+ value: String(amount),
51
+ onChange: (e) => {
52
+ const num = parseInt(e.target.value, 10) || 0;
53
+ onChange(utils.createTimeAgoValue(num, unit));
54
+ },
55
+ disabled,
56
+ size: "S",
57
+ "aria-label": "Time amount"
58
+ }
59
+ ) }),
60
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "120px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
61
+ designSystem.SingleSelect,
62
+ {
63
+ value: unit,
64
+ onChange: (val) => onChange(utils.createTimeAgoValue(amount, val)),
65
+ disabled,
66
+ size: "S",
67
+ children: utils.TIME_UNITS.map((u) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: u.value, children: u.label }, u.value))
68
+ }
69
+ ) })
70
+ ] });
71
+ }
72
+ if (metric.key === "lifecycle_stage") {
73
+ return /* @__PURE__ */ jsxRuntime.jsx(
74
+ designSystem.SingleSelect,
75
+ {
76
+ value: String(value || ""),
77
+ onChange: (val) => onChange(val),
78
+ disabled,
79
+ size: "S",
80
+ children: utils.LIFECYCLE_STAGES.map((stage) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: stage, children: stage.charAt(0).toUpperCase() + stage.slice(1) }, stage))
81
+ }
82
+ );
83
+ }
84
+ if (metric.valueType === "number") {
85
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "120px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
86
+ designSystem.TextInput,
87
+ {
88
+ type: "number",
89
+ value: String(value ?? ""),
90
+ onChange: (e) => {
91
+ const num = parseFloat(e.target.value);
92
+ onChange(isNaN(num) ? 0 : num);
93
+ },
94
+ disabled,
95
+ size: "S",
96
+ "aria-label": "Value"
97
+ }
98
+ ) });
99
+ }
100
+ if (operator === "$in" || operator === "$nin") {
101
+ const arrVal = Array.isArray(value) ? value.join(", ") : String(value || "");
102
+ return /* @__PURE__ */ jsxRuntime.jsx(
103
+ designSystem.TextInput,
104
+ {
105
+ value: arrVal,
106
+ onChange: (e) => {
107
+ const arr = e.target.value.split(",").map((s) => s.trim()).filter(Boolean);
108
+ onChange(arr);
109
+ },
110
+ placeholder: "value1, value2, ...",
111
+ disabled,
112
+ size: "S",
113
+ "aria-label": "Values"
114
+ }
115
+ );
116
+ }
117
+ return /* @__PURE__ */ jsxRuntime.jsx(
118
+ designSystem.TextInput,
119
+ {
120
+ value: String(value ?? ""),
121
+ onChange: (e) => onChange(e.target.value),
122
+ disabled,
123
+ size: "S",
124
+ "aria-label": "Value"
125
+ }
126
+ );
127
+ };
128
+ const RuleRow = ({ rule, onUpdate, onDelete, disabled }) => {
129
+ const theme = styled.useTheme();
130
+ const colors = theme?.colors;
131
+ const metric = utils.getMetricDefinition(rule.field);
132
+ const availableOperators = metric?.operators || utils.OPERATORS.map((o) => o.value);
133
+ const handleFieldChange = (fieldKey) => {
134
+ const newMetric = utils.getMetricDefinition(fieldKey);
135
+ let newValue = 0;
136
+ if (newMetric?.valueType === "boolean") {
137
+ newValue = false;
138
+ } else if (newMetric?.valueType === "time_ago") {
139
+ newValue = { hours_ago: 24 };
140
+ } else if (newMetric?.valueType === "string") {
141
+ newValue = "";
142
+ }
143
+ const newOp = newMetric?.operators[0] || "$eq";
144
+ onUpdate({ ...rule, field: fieldKey, operator: newOp, value: newValue });
145
+ };
146
+ const handleOperatorChange = (op) => {
147
+ let newValue = rule.value;
148
+ if (op === "$exists") {
149
+ newValue = true;
150
+ } else if ((op === "$in" || op === "$nin") && !Array.isArray(rule.value)) {
151
+ newValue = [];
152
+ }
153
+ onUpdate({ ...rule, operator: op, value: newValue });
154
+ };
155
+ return /* @__PURE__ */ jsxRuntime.jsxs(
156
+ designSystem.Flex,
157
+ {
158
+ gap: 2,
159
+ alignItems: "center",
160
+ padding: 2,
161
+ background: "neutral0",
162
+ hasRadius: true,
163
+ style: { border: `1px solid ${colors?.neutral200 || "#dcdce4"}` },
164
+ children: [
165
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: "1 1 200px", minWidth: "180px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
166
+ designSystem.SingleSelect,
167
+ {
168
+ value: rule.field,
169
+ onChange: (val) => handleFieldChange(val),
170
+ disabled,
171
+ size: "S",
172
+ children: Object.entries(utils.METRICS_BY_CATEGORY).map(([category, metrics]) => /* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
173
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: `__header_${category}`, disabled: true, children: utils.CATEGORY_LABELS[category] || category }),
174
+ metrics.map((m) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: m.key, children: m.label }, m.key))
175
+ ] }, category))
176
+ }
177
+ ) }),
178
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "100px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
179
+ designSystem.SingleSelect,
180
+ {
181
+ value: rule.operator,
182
+ onChange: (val) => handleOperatorChange(val),
183
+ disabled,
184
+ size: "S",
185
+ children: utils.OPERATORS.filter((op) => availableOperators.includes(op.value)).map((op) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: op.value, children: op.label }, op.value))
186
+ }
187
+ ) }),
188
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: "1 1 150px", minWidth: "120px" }, children: metric && /* @__PURE__ */ jsxRuntime.jsx(
189
+ ValueInput,
190
+ {
191
+ metric,
192
+ value: rule.value,
193
+ operator: rule.operator,
194
+ onChange: (val) => onUpdate({ ...rule, value: val }),
195
+ disabled
196
+ }
197
+ ) }),
198
+ /* @__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, {}) }) })
199
+ ]
200
+ }
201
+ );
202
+ };
203
+ const RuleGroupComponent = ({
204
+ group,
205
+ onUpdate,
206
+ onDelete,
207
+ depth = 0,
208
+ disabled
209
+ }) => {
210
+ const theme = styled.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];
219
+ const handleAddRule = () => {
220
+ onUpdate(utils.addRuleToGroup(group, group.id, utils.createEmptyRule()));
221
+ };
222
+ const handleAddGroup = () => {
223
+ const newLogic = group.logic === "$and" ? "$or" : "$and";
224
+ onUpdate(utils.addRuleToGroup(group, group.id, utils.createEmptyGroup(newLogic)));
225
+ };
226
+ const handleLogicToggle = () => {
227
+ onUpdate(utils.updateGroupLogic(group, group.id, group.logic === "$and" ? "$or" : "$and"));
228
+ };
229
+ const handleRuleUpdate = (ruleId, updatedRule) => {
230
+ onUpdate(utils.updateRuleInGroup(group, ruleId, () => updatedRule));
231
+ };
232
+ const handleRuleDelete = (ruleId) => {
233
+ onUpdate(utils.deleteRuleFromGroup(group, ruleId));
234
+ };
235
+ const handleNestedGroupUpdate = (nestedGroup) => {
236
+ onUpdate({
237
+ ...group,
238
+ rules: group.rules.map(
239
+ (item) => utils.isRuleGroup(item) && item.id === nestedGroup.id ? nestedGroup : item
240
+ )
241
+ });
242
+ };
243
+ const handleNestedGroupDelete = (groupId) => {
244
+ onUpdate(utils.deleteGroupFromParent(group, groupId));
245
+ };
246
+ return /* @__PURE__ */ jsxRuntime.jsxs(
247
+ designSystem.Box,
248
+ {
249
+ padding: 3,
250
+ background: depth === 0 ? "neutral100" : "neutral0",
251
+ hasRadius: true,
252
+ style: {
253
+ borderLeft: `3px solid ${borderColor}`,
254
+ marginLeft: depth > 0 ? "12px" : 0
255
+ },
256
+ 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" ? "All conditions must match" : "Any condition can match" })
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) => {
274
+ if (utils.isRuleGroup(item)) {
275
+ return /* @__PURE__ */ jsxRuntime.jsx(
276
+ RuleGroupComponent,
277
+ {
278
+ group: item,
279
+ onUpdate: handleNestedGroupUpdate,
280
+ onDelete: () => handleNestedGroupDelete(item.id),
281
+ depth: depth + 1,
282
+ disabled
283
+ },
284
+ item.id
285
+ );
286
+ }
287
+ return /* @__PURE__ */ jsxRuntime.jsx(
288
+ RuleRow,
289
+ {
290
+ rule: item,
291
+ onUpdate: (updated) => handleRuleUpdate(item.id, updated),
292
+ onDelete: () => handleRuleDelete(item.id),
293
+ disabled
294
+ },
295
+ item.id
296
+ );
297
+ }) }),
298
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, marginTop: 3, children: [
299
+ /* @__PURE__ */ jsxRuntime.jsx(
300
+ designSystem.Button,
301
+ {
302
+ variant: "secondary",
303
+ size: "S",
304
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
305
+ onClick: handleAddRule,
306
+ disabled,
307
+ children: "Add condition"
308
+ }
309
+ ),
310
+ depth < 2 && /* @__PURE__ */ jsxRuntime.jsxs(
311
+ designSystem.Button,
312
+ {
313
+ variant: "tertiary",
314
+ size: "S",
315
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Layout, {}),
316
+ onClick: handleAddGroup,
317
+ disabled,
318
+ children: [
319
+ "Add ",
320
+ group.logic === "$and" ? "OR" : "AND",
321
+ " group"
322
+ ]
323
+ }
324
+ )
325
+ ] })
326
+ ]
327
+ }
328
+ );
329
+ };
330
+ const RulesBuilder = React.forwardRef(
331
+ ({ name, value, onChange, intlLabel, disabled, error, required, hint }, ref) => {
332
+ const [config, setConfig] = React.useState(() => utils.deserializeConfig(value));
333
+ React.useEffect(() => {
334
+ setConfig(utils.deserializeConfig(value));
335
+ }, [value]);
336
+ const displayLabel = React.useMemo(() => {
337
+ if (intlLabel?.defaultMessage && !intlLabel.defaultMessage.includes(".")) {
338
+ return intlLabel.defaultMessage;
339
+ }
340
+ return "Segment Rules";
341
+ }, [intlLabel]);
342
+ const rulesCount = React.useMemo(() => utils.countRules(config), [config]);
343
+ const handleConfigUpdate = React.useCallback(
344
+ (newConfig) => {
345
+ setConfig(newConfig);
346
+ onChange({ target: { name, value: utils.serializeConfig(newConfig) } });
347
+ },
348
+ [name, onChange]
349
+ );
350
+ const handleClear = () => {
351
+ handleConfigUpdate(utils.createInitialConfig());
352
+ };
353
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name, error, hint, required, children: [
354
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
355
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: displayLabel }),
356
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
357
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { children: [
358
+ rulesCount,
359
+ " rule",
360
+ rulesCount !== 1 ? "s" : ""
361
+ ] }),
362
+ rulesCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "ghost", size: "S", onClick: handleClear, disabled, children: "Clear all" })
363
+ ] })
364
+ ] }),
365
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 2, ref, children: /* @__PURE__ */ jsxRuntime.jsx(
366
+ RuleGroupComponent,
367
+ {
368
+ group: config,
369
+ onUpdate: handleConfigUpdate,
370
+ depth: 0,
371
+ disabled
372
+ }
373
+ ) }),
374
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {}),
375
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
376
+ ] });
377
+ }
378
+ );
379
+ RulesBuilder.displayName = "RulesBuilder";
380
+ exports.default = RulesBuilder;
@@ -0,0 +1,181 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useState, useEffect, useMemo } from "react";
3
+ import { Field, Flex, Typography, Box, TextInput, Tooltip, IconButton, Button, Badge } from "@strapi/design-system";
4
+ import { ArrowUp, ArrowDown, Trash, Plus } from "@strapi/icons";
5
+ import { m as generateId } from "./utils-C6_ndVAZ.mjs";
6
+ const parseButtons = (value) => {
7
+ if (!value) return [];
8
+ if (Array.isArray(value)) {
9
+ return value.map((b) => ({
10
+ id: typeof b?.id === "string" ? b.id : 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
+ }));
15
+ }
16
+ if (typeof value === "string") {
17
+ try {
18
+ const parsed = JSON.parse(value);
19
+ if (!Array.isArray(parsed)) return [];
20
+ return parsed.map((b) => ({
21
+ id: typeof b?.id === "string" ? b.id : generateId(),
22
+ text: typeof b?.text === "string" ? b.text : "",
23
+ url: typeof b?.url === "string" ? b.url : "",
24
+ row: typeof b?.row === "number" ? b.row : 0
25
+ }));
26
+ } catch {
27
+ return [];
28
+ }
29
+ }
30
+ return [];
31
+ };
32
+ const serializeButtons = (buttons) => JSON.stringify(buttons);
33
+ const isValidUrl = (url) => {
34
+ try {
35
+ const u = new URL(url);
36
+ return u.protocol === "http:" || u.protocol === "https:";
37
+ } catch {
38
+ return false;
39
+ }
40
+ };
41
+ const ButtonsBuilder = forwardRef(
42
+ ({ name, value, onChange, intlLabel, disabled, error, required, hint }, ref) => {
43
+ const [buttons, setButtons] = useState(() => parseButtons(value));
44
+ useEffect(() => {
45
+ setButtons(parseButtons(value));
46
+ }, [value]);
47
+ const update = (next) => {
48
+ setButtons(next);
49
+ onChange({ target: { name, value: serializeButtons(next) } });
50
+ };
51
+ const addButton = () => {
52
+ update([
53
+ ...buttons,
54
+ { id: generateId(), text: "Button", url: "https://cases.gg", row: 0 }
55
+ ]);
56
+ };
57
+ const updateButton = (id, patch) => {
58
+ update(buttons.map((b) => b.id === id ? { ...b, ...patch } : b));
59
+ };
60
+ const deleteButton = (id) => {
61
+ update(buttons.filter((b) => b.id !== id));
62
+ };
63
+ const move = (from, to) => {
64
+ if (to < 0 || to >= buttons.length) return;
65
+ const copy = [...buttons];
66
+ const [item] = copy.splice(from, 1);
67
+ copy.splice(to, 0, item);
68
+ update(copy);
69
+ };
70
+ const previewRows = useMemo(() => {
71
+ const rows = /* @__PURE__ */ new Map();
72
+ for (const b of buttons) {
73
+ const row = b.row ?? 0;
74
+ rows.set(row, [...rows.get(row) || [], b]);
75
+ }
76
+ return Array.from(rows.entries()).sort((a, b) => a[0] - b[0]).map(([row, items]) => ({ row, items }));
77
+ }, [buttons]);
78
+ return /* @__PURE__ */ jsx(Field.Root, { name, error, required, hint, ref, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
79
+ /* @__PURE__ */ jsx(Field.Label, { children: intlLabel?.defaultMessage || "Buttons" }),
80
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Build Telegram inline keyboard buttons (text + URL). Stored as JSON." }),
81
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
82
+ buttons.map((btn, idx) => {
83
+ const urlOk = btn.url.length === 0 ? true : isValidUrl(btn.url);
84
+ return /* @__PURE__ */ jsxs(
85
+ Flex,
86
+ {
87
+ gap: 2,
88
+ alignItems: "flex-start",
89
+ padding: 3,
90
+ background: "neutral0",
91
+ hasRadius: true,
92
+ style: { border: "1px solid #dcdce4" },
93
+ children: [
94
+ /* @__PURE__ */ jsx(Box, { style: { flex: 2, minWidth: 180 }, children: /* @__PURE__ */ jsxs(Field.Root, { name: `${name}.${btn.id}.text`, children: [
95
+ /* @__PURE__ */ jsx(Field.Label, { children: "Text" }),
96
+ /* @__PURE__ */ jsx(
97
+ TextInput,
98
+ {
99
+ value: btn.text,
100
+ onChange: (e) => updateButton(btn.id, { text: e.target.value }),
101
+ disabled
102
+ }
103
+ )
104
+ ] }) }),
105
+ /* @__PURE__ */ jsx(Box, { style: { flex: 3, minWidth: 220 }, children: /* @__PURE__ */ jsxs(Field.Root, { name: `${name}.${btn.id}.url`, children: [
106
+ /* @__PURE__ */ jsx(Field.Label, { children: "URL" }),
107
+ /* @__PURE__ */ jsx(
108
+ TextInput,
109
+ {
110
+ value: btn.url,
111
+ onChange: (e) => updateButton(btn.id, { url: e.target.value }),
112
+ disabled
113
+ }
114
+ ),
115
+ !urlOk && /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "danger600", style: { marginTop: 4 }, children: "Please enter a valid http/https URL" })
116
+ ] }) }),
117
+ /* @__PURE__ */ jsx(Box, { style: { width: 90 }, children: /* @__PURE__ */ jsxs(Field.Root, { name: `${name}.${btn.id}.row`, children: [
118
+ /* @__PURE__ */ jsx(Field.Label, { children: "Row" }),
119
+ /* @__PURE__ */ jsx(
120
+ TextInput,
121
+ {
122
+ type: "number",
123
+ value: String(btn.row ?? 0),
124
+ onChange: (e) => updateButton(btn.id, { row: parseInt(e.target.value, 10) || 0 }),
125
+ disabled
126
+ }
127
+ )
128
+ ] }) }),
129
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 1, paddingTop: 6, children: [
130
+ /* @__PURE__ */ jsx(Tooltip, { label: "Move up", children: /* @__PURE__ */ jsx(
131
+ IconButton,
132
+ {
133
+ onClick: () => move(idx, idx - 1),
134
+ label: "Move up",
135
+ variant: "ghost",
136
+ disabled: disabled || idx === 0,
137
+ children: /* @__PURE__ */ jsx(ArrowUp, {})
138
+ }
139
+ ) }),
140
+ /* @__PURE__ */ jsx(Tooltip, { label: "Move down", children: /* @__PURE__ */ jsx(
141
+ IconButton,
142
+ {
143
+ onClick: () => move(idx, idx + 1),
144
+ label: "Move down",
145
+ variant: "ghost",
146
+ disabled: disabled || idx === buttons.length - 1,
147
+ children: /* @__PURE__ */ jsx(ArrowDown, {})
148
+ }
149
+ ) }),
150
+ /* @__PURE__ */ jsx(Tooltip, { label: "Delete button", children: /* @__PURE__ */ jsx(
151
+ IconButton,
152
+ {
153
+ onClick: () => deleteButton(btn.id),
154
+ label: "Delete button",
155
+ variant: "ghost",
156
+ disabled,
157
+ children: /* @__PURE__ */ jsx(Trash, {})
158
+ }
159
+ ) })
160
+ ] })
161
+ ]
162
+ },
163
+ btn.id
164
+ );
165
+ }),
166
+ buttons.length === 0 && /* @__PURE__ */ jsx(Box, { padding: 4, background: "neutral0", hasRadius: true, style: { border: "1px dashed #dcdce4" }, children: /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral600", children: "No buttons. Add one to create an inline keyboard." }) })
167
+ ] }),
168
+ /* @__PURE__ */ jsx(Flex, { gap: 2, children: /* @__PURE__ */ jsx(Button, { startIcon: /* @__PURE__ */ jsx(Plus, {}), onClick: addButton, disabled, variant: "secondary", children: "Add button" }) }),
169
+ buttons.length > 0 && /* @__PURE__ */ jsxs(Box, { paddingTop: 2, children: [
170
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { marginBottom: 8, display: "block" }, children: "Preview" }),
171
+ /* @__PURE__ */ jsx(Flex, { direction: "column", gap: 2, children: previewRows.map((row) => /* @__PURE__ */ jsx(Flex, { gap: 1, wrap: "wrap", children: row.items.map((b) => /* @__PURE__ */ jsx(Badge, { backgroundColor: "primary100", children: b.text || "(empty)" }, b.id)) }, row.row)) })
172
+ ] }),
173
+ error && /* @__PURE__ */ jsx(Field.Error, { children: error }),
174
+ hint && /* @__PURE__ */ jsx(Field.Hint, { children: hint })
175
+ ] }) });
176
+ }
177
+ );
178
+ ButtonsBuilder.displayName = "ButtonsBuilder";
179
+ export {
180
+ ButtonsBuilder as default
181
+ };
@@ -96,15 +96,33 @@ const DEFAULT_CONFIG = {
96
96
  };
97
97
  const parseConfig = (value) => {
98
98
  if (!value) return DEFAULT_CONFIG;
99
- try {
100
- const parsed = JSON.parse(value);
101
- if (parsed.logic && Array.isArray(parsed.rules)) {
102
- return parsed;
99
+ if (typeof value === "string") {
100
+ try {
101
+ const parsed = JSON.parse(value);
102
+ if (parsed.logic && Array.isArray(parsed.rules)) {
103
+ return {
104
+ ...parsed,
105
+ rules: parsed.rules.map((rule) => ({
106
+ ...rule,
107
+ id: rule.id || generateId()
108
+ }))
109
+ };
110
+ }
111
+ return DEFAULT_CONFIG;
112
+ } catch {
113
+ return DEFAULT_CONFIG;
103
114
  }
104
- return DEFAULT_CONFIG;
105
- } catch {
106
- return DEFAULT_CONFIG;
107
115
  }
116
+ if (value.logic && Array.isArray(value.rules)) {
117
+ return {
118
+ ...value,
119
+ rules: value.rules.map((rule) => ({
120
+ ...rule,
121
+ id: rule.id || generateId()
122
+ }))
123
+ };
124
+ }
125
+ return DEFAULT_CONFIG;
108
126
  };
109
127
  const serializeConfig = (config) => {
110
128
  return JSON.stringify(config);