@proveanything/smartlinks-utils-ui 0.1.0

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,780 @@
1
+ import { cn } from './chunk-L7FQ52F5.js';
2
+ import { useState, useCallback } from 'react';
3
+ import { GitBranch, Plus, Filter, ChevronRight, X, RefreshCw, Trash2, ChevronDown, ToggleLeft, Package, MapPin, Tag, Monitor, Calendar, User, Hash, Globe, Tags } from 'lucide-react';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+
6
+ var CONDITION_TYPES = [
7
+ { value: "version", title: "Version", description: "Match specific versions", icon: "tags", color: "#7c3aed", helpText: "Limit by specific versions of your application or product." },
8
+ { value: "country", title: "Country", description: "Filter by geographic location", icon: "globe", color: "#2563eb", helpText: "Limit by the user's country or region." },
9
+ { value: "value", title: "Value", description: "Match custom field values", icon: "hash", color: "#ea580c", helpText: "Compare text, numbers, or boolean values using various operators." },
10
+ { value: "user", title: "User", description: "Filter by auth & permissions", icon: "user", color: "#16a34a", helpText: "Limit by authentication status, ownership, group membership, or admin privileges." },
11
+ { value: "date", title: "Date", description: "Time-based conditions", icon: "calendar", color: "#dc2626", helpText: "Limit by specific dates or date ranges." },
12
+ { value: "device", title: "Device", description: "Filter by device type", icon: "monitor", color: "#0891b2", helpText: "Limit by the type of device or operating system." },
13
+ { value: "tag", title: "Label", description: "Match by tags or labels", icon: "tag", color: "#db2777", helpText: "Limit by assigned labels or tags." },
14
+ { value: "geofence", title: "Geofence", description: "Geographic boundary", icon: "map-pin", color: "#0d9488", helpText: "Limit by geographic boundaries (rectangular area)." },
15
+ { value: "product", title: "Product ID", description: "Match specific items", icon: "package", color: "#d97706", helpText: "Limit by specific product IDs." },
16
+ { value: "itemStatus", title: "Item Status", description: "Filter by item state", icon: "toggle-left", color: "#ea580c", helpText: "Limit by item status such as proof, claimable, owner, or virtual." },
17
+ { value: "condition", title: "Condition", description: "Reference another condition", icon: "git-branch", color: "#4f46e5", helpText: "Reference another saved condition and check if it passes or fails." }
18
+ ];
19
+ var iconMap = {
20
+ tags: Tags,
21
+ globe: Globe,
22
+ hash: Hash,
23
+ user: User,
24
+ calendar: Calendar,
25
+ monitor: Monitor,
26
+ tag: Tag,
27
+ "map-pin": MapPin,
28
+ package: Package,
29
+ "toggle-left": ToggleLeft,
30
+ "git-branch": GitBranch
31
+ };
32
+ function getConditionMeta(type) {
33
+ if (!type) return void 0;
34
+ return CONDITION_TYPES.find((t) => t.value === type);
35
+ }
36
+ function getConditionIcon(type) {
37
+ const meta = getConditionMeta(type);
38
+ return meta && iconMap[meta.icon] || Hash;
39
+ }
40
+ function getConditionSummary(cond, options) {
41
+ if (!cond.type) return "No condition type selected";
42
+ const meta = getConditionMeta(cond.type);
43
+ let summary = meta?.title || cond.type;
44
+ switch (cond.type) {
45
+ case "version":
46
+ if (cond.versions?.length) {
47
+ const verb = cond.contains ? "is" : "is not";
48
+ summary += ` ${verb} ${cond.versions.map((v) => typeof v === "string" ? v : v.title || v.value || "").join(", ")}`;
49
+ }
50
+ break;
51
+ case "country":
52
+ if (cond.useRegions && cond.regions?.length) {
53
+ const verb = cond.contains ? "is in" : "is not in";
54
+ const names = cond.regions.map((r) => {
55
+ const reg = REGION_OPTIONS.find((o) => o.value === r);
56
+ return reg?.title || r;
57
+ });
58
+ summary += ` ${verb} ${names.join(", ")}`;
59
+ } else if (cond.countries?.length) {
60
+ const verb = cond.contains ? "is" : "is not";
61
+ summary += ` ${verb} ${cond.countries.join(", ")}`;
62
+ }
63
+ break;
64
+ case "value":
65
+ if (cond.field) {
66
+ const ops = { equal: "=", not: "\u2260", greater: ">", less: "<" };
67
+ summary += `: ${cond.field} ${ops[cond.validationType || ""] || ""} ${cond.value ?? "?"}`;
68
+ }
69
+ break;
70
+ case "tag":
71
+ if (cond.tags?.length) {
72
+ const verb = cond.contains ? "has" : "does not have";
73
+ summary += ` ${verb} ${cond.tags.length} label(s)`;
74
+ }
75
+ break;
76
+ case "device":
77
+ if (cond.displays?.length) {
78
+ const verb = cond.contains ? "is" : "is not";
79
+ summary += ` ${verb} ${cond.displays.join(", ")}`;
80
+ }
81
+ break;
82
+ case "user": {
83
+ const userTypes = {
84
+ valid: "is logged in",
85
+ invalid: "is logged out",
86
+ owner: "is item owner",
87
+ group: cond.groupIds?.length ? `in ${cond.groupIds.length} group(s)` : "in groups",
88
+ admin: "is admin"
89
+ };
90
+ summary += ` ${userTypes[cond.userType || ""] || ""}`;
91
+ break;
92
+ }
93
+ case "product":
94
+ if (cond.productIds?.length) {
95
+ const verb = cond.contains ? "is" : "is not";
96
+ summary += ` ${verb} ${cond.productIds.length} item(s)`;
97
+ }
98
+ break;
99
+ case "condition":
100
+ if (cond.conditionId) {
101
+ const condRef = options?.savedConditions?.find((c) => c.value === cond.conditionId);
102
+ summary += `: ${condRef?.title || cond.conditionId} ${cond.passes ? "passes" : "fails"}`;
103
+ }
104
+ break;
105
+ case "date": {
106
+ const dateTests = { before: "before", after: "after", between: "between" };
107
+ summary += ` is ${dateTests[cond.dateTest || ""] || ""}`;
108
+ if (cond.beforeDate) summary += ` ${cond.beforeDate}`;
109
+ if (cond.afterDate) summary += ` ${cond.afterDate}`;
110
+ if (cond.rangeDate) summary += ` ${cond.rangeDate}`;
111
+ break;
112
+ }
113
+ case "geofence": {
114
+ const verb = cond.contains ? "inside" : "outside";
115
+ summary += ` ${verb} bounds`;
116
+ break;
117
+ }
118
+ case "itemStatus": {
119
+ const statusTypes = {
120
+ hasProof: "has proof",
121
+ noProof: "has no proof",
122
+ isClaimable: "is claimable",
123
+ notClaimable: "is not claimable",
124
+ hasOwner: "has owner",
125
+ isVirtual: "is virtual"
126
+ };
127
+ summary += ` ${statusTypes[cond.statusType || ""] || ""}`;
128
+ break;
129
+ }
130
+ }
131
+ return summary;
132
+ }
133
+ var DISPLAY_OPTIONS = [
134
+ { title: "Desktop", value: "desktop" },
135
+ { title: "Mobile", value: "mobile" },
136
+ { title: "Android", value: "android" },
137
+ { title: "iOS", value: "ios" },
138
+ { title: "Apple Mac", value: "mac" },
139
+ { title: "Microsoft Windows", value: "win" }
140
+ ];
141
+ var REGION_OPTIONS = [
142
+ { title: "European Union (EU)", value: "eu", description: "All 27 EU member states" },
143
+ { title: "European Economic Area (EEA)", value: "eea", description: "EU + Iceland, Liechtenstein, Norway" },
144
+ { title: "United Kingdom", value: "uk", description: "England, Scotland, Wales, Northern Ireland" },
145
+ { title: "North America", value: "northamerica", description: "USA, Canada, Mexico" },
146
+ { title: "Asia Pacific", value: "asiapacific", description: "Major Asia-Pacific markets" }
147
+ ];
148
+ var USER_TYPE_OPTIONS = [
149
+ { title: "Is Logged In", value: "valid" },
150
+ { title: "Is Logged Out", value: "invalid" },
151
+ { title: "Is Item Owner", value: "owner" },
152
+ { title: "Is Group Member", value: "group" },
153
+ { title: "Is Admin", value: "admin" }
154
+ ];
155
+ var ITEM_STATUS_OPTIONS = [
156
+ { title: "Is Product (Not Item)", value: "hasProof" },
157
+ { title: "Is Single Item", value: "noProof" },
158
+ { title: "Is Item & Claimable", value: "isClaimable" },
159
+ { title: "Is Item & Claimed", value: "notClaimable" },
160
+ { title: "Is Virtual Item", value: "isVirtual" },
161
+ { title: "Is Not A Virtual Item", value: "notVirtual" }
162
+ ];
163
+ var VALUE_OPERATORS = [
164
+ { title: "Equal to", value: "equal" },
165
+ { title: "Not Equal", value: "not" },
166
+ { title: "Greater Than", value: "greater" },
167
+ { title: "Less Than", value: "less" }
168
+ ];
169
+ var FIELD_TYPES = [
170
+ { title: "Boolean", value: "boolean" },
171
+ { title: "Integer", value: "integer" },
172
+ { title: "Text", value: "text" }
173
+ ];
174
+ var ConditionTypePicker = ({ onSelect }) => {
175
+ return /* @__PURE__ */ jsxs("div", { className: "p-4", children: [
176
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-gray-500 dark:text-gray-400 mb-3", children: "Choose a condition type:" }),
177
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 sm:grid-cols-4 gap-2", children: CONDITION_TYPES.map((ct) => {
178
+ const Icon = getConditionIcon(ct.value);
179
+ return /* @__PURE__ */ jsxs(
180
+ "button",
181
+ {
182
+ onClick: () => onSelect(ct.value),
183
+ className: cn(
184
+ "flex flex-col items-center gap-1.5 p-3 rounded-lg border-2 border-gray-200 dark:border-gray-700",
185
+ "hover:border-blue-400 dark:hover:border-blue-500 hover:bg-blue-50/50 dark:hover:bg-blue-950/20",
186
+ "transition-all text-center cursor-pointer"
187
+ ),
188
+ children: [
189
+ /* @__PURE__ */ jsx(
190
+ "div",
191
+ {
192
+ className: "w-8 h-8 rounded-full flex items-center justify-center",
193
+ style: { backgroundColor: `${ct.color}18`, color: ct.color },
194
+ children: /* @__PURE__ */ jsx(Icon, { className: "w-4 h-4" })
195
+ }
196
+ ),
197
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-gray-700 dark:text-gray-300", children: ct.title })
198
+ ]
199
+ },
200
+ ct.value
201
+ );
202
+ }) })
203
+ ] });
204
+ };
205
+ var ContainsToggle = ({ value = true, onChange, trueLabel = "Is One Of", falseLabel = "Is Not One Of", disabled }) => /* @__PURE__ */ jsx("div", { className: "flex gap-1 bg-gray-100 dark:bg-gray-800 rounded-md p-0.5 w-fit", children: [{ v: true, label: trueLabel }, { v: false, label: falseLabel }].map((opt) => /* @__PURE__ */ jsx(
206
+ "button",
207
+ {
208
+ onClick: () => onChange(opt.v),
209
+ disabled,
210
+ className: cn(
211
+ "px-2.5 py-1 text-xs font-medium rounded transition-colors",
212
+ value === opt.v ? "bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm" : "text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
213
+ ),
214
+ children: opt.label
215
+ },
216
+ String(opt.v)
217
+ )) });
218
+ var ChipSelect = ({ options, selected, onChange, disabled }) => /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: options.map((opt) => {
219
+ const isSelected = selected.includes(opt.value);
220
+ return /* @__PURE__ */ jsx(
221
+ "button",
222
+ {
223
+ onClick: () => {
224
+ if (disabled) return;
225
+ onChange(isSelected ? selected.filter((v) => v !== opt.value) : [...selected, opt.value]);
226
+ },
227
+ disabled,
228
+ title: opt.description,
229
+ className: cn(
230
+ "px-2.5 py-1 text-xs rounded-full border transition-colors",
231
+ isSelected ? "bg-blue-100 dark:bg-blue-900/40 border-blue-300 dark:border-blue-700 text-blue-700 dark:text-blue-300" : "border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:border-gray-300 dark:hover:border-gray-600"
232
+ ),
233
+ children: opt.title
234
+ },
235
+ opt.value
236
+ );
237
+ }) });
238
+ var inputClass = "w-full px-2.5 py-1.5 text-sm rounded-md border border-gray-300 dark:border-gray-600 bg-transparent focus:outline-none focus:ring-1 focus:ring-blue-500";
239
+ var ConditionConfig = ({
240
+ condition: cond,
241
+ onChange,
242
+ editorProps,
243
+ readOnly
244
+ }) => {
245
+ const update = (patch) => onChange({ ...cond, ...patch });
246
+ switch (cond.type) {
247
+ // ─── VERSION ──────────────────────────────────
248
+ case "version":
249
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
250
+ /* @__PURE__ */ jsx(ContainsToggle, { value: cond.contains, onChange: (v) => update({ contains: v }), disabled: readOnly }),
251
+ editorProps.versions?.length ? /* @__PURE__ */ jsx(
252
+ ChipSelect,
253
+ {
254
+ options: editorProps.versions.map((v) => ({ title: v.title, value: v.value || "" })),
255
+ selected: (cond.versions || []).map((v) => typeof v === "string" ? v : v.value || ""),
256
+ onChange: (vals) => update({ versions: vals.map((v) => ({ value: v, title: editorProps.versions?.find((o) => o.value === v)?.title || v })) }),
257
+ disabled: readOnly
258
+ }
259
+ ) : /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 italic", children: "No versions available" })
260
+ ] });
261
+ // ─── COUNTRY ──────────────────────────────────
262
+ case "country":
263
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
264
+ /* @__PURE__ */ jsx(ContainsToggle, { value: cond.contains, onChange: (v) => update({ contains: v }), disabled: readOnly }),
265
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
266
+ /* @__PURE__ */ jsx("label", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Use regions" }),
267
+ /* @__PURE__ */ jsx(
268
+ "button",
269
+ {
270
+ onClick: () => update({ useRegions: !cond.useRegions }),
271
+ disabled: readOnly,
272
+ className: cn(
273
+ "w-8 h-5 rounded-full transition-colors relative",
274
+ cond.useRegions ? "bg-blue-500" : "bg-gray-300 dark:bg-gray-600"
275
+ ),
276
+ children: /* @__PURE__ */ jsx("span", { className: cn(
277
+ "absolute top-0.5 w-4 h-4 rounded-full bg-white shadow transition-transform",
278
+ cond.useRegions ? "translate-x-3.5" : "translate-x-0.5"
279
+ ) })
280
+ }
281
+ )
282
+ ] }),
283
+ cond.useRegions ? /* @__PURE__ */ jsx(
284
+ ChipSelect,
285
+ {
286
+ options: REGION_OPTIONS,
287
+ selected: cond.regions || [],
288
+ onChange: (vals) => update({ regions: vals }),
289
+ disabled: readOnly
290
+ }
291
+ ) : /* @__PURE__ */ jsx(
292
+ "input",
293
+ {
294
+ className: inputClass,
295
+ value: (cond.countries || []).join(", "),
296
+ onChange: (e) => update({ countries: e.target.value.split(",").map((s) => s.trim()).filter(Boolean) }),
297
+ placeholder: "US, GB, DE, \u2026",
298
+ disabled: readOnly
299
+ }
300
+ )
301
+ ] });
302
+ // ─── VALUE ────────────────────────────────────
303
+ case "value":
304
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
305
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
306
+ /* @__PURE__ */ jsxs("div", { children: [
307
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] text-gray-400 mb-0.5 block", children: "Field Name" }),
308
+ /* @__PURE__ */ jsx("input", { className: inputClass, value: cond.field || "", onChange: (e) => update({ field: e.target.value }), placeholder: "field_name", disabled: readOnly })
309
+ ] }),
310
+ /* @__PURE__ */ jsxs("div", { children: [
311
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] text-gray-400 mb-0.5 block", children: "Field Type" }),
312
+ /* @__PURE__ */ jsx("select", { className: inputClass, value: cond.fieldType || "text", onChange: (e) => update({ fieldType: e.target.value }), disabled: readOnly, children: FIELD_TYPES.map((ft) => /* @__PURE__ */ jsx("option", { value: ft.value, children: ft.title }, ft.value)) })
313
+ ] })
314
+ ] }),
315
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
316
+ /* @__PURE__ */ jsxs("div", { children: [
317
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] text-gray-400 mb-0.5 block", children: "Operator" }),
318
+ /* @__PURE__ */ jsx("select", { className: inputClass, value: cond.validationType || "equal", onChange: (e) => update({ validationType: e.target.value }), disabled: readOnly, children: VALUE_OPERATORS.map((op) => /* @__PURE__ */ jsx("option", { value: op.value, children: op.title }, op.value)) })
319
+ ] }),
320
+ /* @__PURE__ */ jsxs("div", { children: [
321
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] text-gray-400 mb-0.5 block", children: "Value" }),
322
+ /* @__PURE__ */ jsx("input", { className: inputClass, value: String(cond.value ?? ""), onChange: (e) => update({ value: e.target.value }), placeholder: "value", disabled: readOnly })
323
+ ] })
324
+ ] })
325
+ ] });
326
+ // ─── USER ─────────────────────────────────────
327
+ case "user":
328
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
329
+ /* @__PURE__ */ jsx(
330
+ ChipSelect,
331
+ {
332
+ options: USER_TYPE_OPTIONS,
333
+ selected: cond.userType ? [cond.userType] : [],
334
+ onChange: (vals) => update({ userType: vals[vals.length - 1] || void 0 }),
335
+ disabled: readOnly
336
+ }
337
+ ),
338
+ cond.userType === "group" && editorProps.userGroups?.length ? /* @__PURE__ */ jsx(
339
+ ChipSelect,
340
+ {
341
+ options: editorProps.userGroups,
342
+ selected: cond.groupIds || [],
343
+ onChange: (vals) => update({ groupIds: vals }),
344
+ disabled: readOnly
345
+ }
346
+ ) : null
347
+ ] });
348
+ // ─── DATE ─────────────────────────────────────
349
+ case "date":
350
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
351
+ /* @__PURE__ */ jsx("div", { className: "flex gap-1 bg-gray-100 dark:bg-gray-800 rounded-md p-0.5 w-fit", children: ["before", "after", "between"].map((dt) => /* @__PURE__ */ jsxs(
352
+ "button",
353
+ {
354
+ onClick: () => update({ dateTest: dt }),
355
+ disabled: readOnly,
356
+ className: cn(
357
+ "px-2.5 py-1 text-xs font-medium rounded transition-colors capitalize",
358
+ cond.dateTest === dt ? "bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm" : "text-gray-500 dark:text-gray-400"
359
+ ),
360
+ children: [
361
+ "Is ",
362
+ dt
363
+ ]
364
+ },
365
+ dt
366
+ )) }),
367
+ (cond.dateTest === "before" || cond.dateTest === "between") && /* @__PURE__ */ jsxs("div", { children: [
368
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] text-gray-400 mb-0.5 block", children: cond.dateTest === "between" ? "Start Date" : "Before Date" }),
369
+ /* @__PURE__ */ jsx("input", { type: "date", className: inputClass, value: cond.beforeDate || "", onChange: (e) => update({ beforeDate: e.target.value }), disabled: readOnly })
370
+ ] }),
371
+ (cond.dateTest === "after" || cond.dateTest === "between") && /* @__PURE__ */ jsxs("div", { children: [
372
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] text-gray-400 mb-0.5 block", children: cond.dateTest === "between" ? "End Date" : "After Date" }),
373
+ /* @__PURE__ */ jsx("input", { type: "date", className: inputClass, value: cond.afterDate || "", onChange: (e) => update({ afterDate: e.target.value }), disabled: readOnly })
374
+ ] })
375
+ ] });
376
+ // ─── DEVICE ───────────────────────────────────
377
+ case "device":
378
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
379
+ /* @__PURE__ */ jsx(ContainsToggle, { value: cond.contains, onChange: (v) => update({ contains: v }), disabled: readOnly }),
380
+ /* @__PURE__ */ jsx(
381
+ ChipSelect,
382
+ {
383
+ options: DISPLAY_OPTIONS,
384
+ selected: cond.displays || [],
385
+ onChange: (vals) => update({ displays: vals }),
386
+ disabled: readOnly
387
+ }
388
+ )
389
+ ] });
390
+ // ─── TAG ──────────────────────────────────────
391
+ case "tag":
392
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
393
+ /* @__PURE__ */ jsx(ContainsToggle, { value: cond.contains, onChange: (v) => update({ contains: v }), disabled: readOnly }),
394
+ editorProps.tags?.length ? /* @__PURE__ */ jsx(
395
+ ChipSelect,
396
+ {
397
+ options: editorProps.tags.map((t) => ({ title: t, value: t })),
398
+ selected: cond.tags || [],
399
+ onChange: (vals) => update({ tags: vals }),
400
+ disabled: readOnly
401
+ }
402
+ ) : /* @__PURE__ */ jsx("input", { className: inputClass, value: (cond.tags || []).join(", "), onChange: (e) => update({ tags: e.target.value.split(",").map((s) => s.trim()).filter(Boolean) }), placeholder: "tag1, tag2, \u2026", disabled: readOnly })
403
+ ] });
404
+ // ─── GEOFENCE ─────────────────────────────────
405
+ case "geofence":
406
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
407
+ /* @__PURE__ */ jsx(ContainsToggle, { value: cond.contains, onChange: (v) => update({ contains: v }), trueLabel: "Is Inside Of", falseLabel: "Is Outside Of", disabled: readOnly }),
408
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2", children: [
409
+ { label: "Latitude 1 (NW)", key: "lat1" },
410
+ { label: "Longitude 1 (NW)", key: "lng1" },
411
+ { label: "Latitude 2 (SE)", key: "lat2" },
412
+ { label: "Longitude 2 (SE)", key: "lng2" }
413
+ ].map((f) => /* @__PURE__ */ jsxs("div", { children: [
414
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] text-gray-400 mb-0.5 block", children: f.label }),
415
+ /* @__PURE__ */ jsx(
416
+ "input",
417
+ {
418
+ type: "number",
419
+ step: "any",
420
+ className: inputClass,
421
+ value: cond[f.key] ?? "",
422
+ onChange: (e) => update({ [f.key]: e.target.value ? parseFloat(e.target.value) : void 0 }),
423
+ disabled: readOnly
424
+ }
425
+ )
426
+ ] }, f.key)) })
427
+ ] });
428
+ // ─── PRODUCT ──────────────────────────────────
429
+ case "product":
430
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
431
+ /* @__PURE__ */ jsx(ContainsToggle, { value: cond.contains, onChange: (v) => update({ contains: v }), disabled: readOnly }),
432
+ editorProps.products?.length ? /* @__PURE__ */ jsx(
433
+ ChipSelect,
434
+ {
435
+ options: editorProps.products,
436
+ selected: cond.productIds || [],
437
+ onChange: (vals) => update({ productIds: vals }),
438
+ disabled: readOnly
439
+ }
440
+ ) : /* @__PURE__ */ jsx("input", { className: inputClass, value: (cond.productIds || []).join(", "), onChange: (e) => update({ productIds: e.target.value.split(",").map((s) => s.trim()).filter(Boolean) }), placeholder: "product-id-1, product-id-2, \u2026", disabled: readOnly })
441
+ ] });
442
+ // ─── ITEM STATUS ──────────────────────────────
443
+ case "itemStatus":
444
+ return /* @__PURE__ */ jsx(
445
+ ChipSelect,
446
+ {
447
+ options: ITEM_STATUS_OPTIONS,
448
+ selected: cond.statusType ? [cond.statusType] : [],
449
+ onChange: (vals) => update({ statusType: vals[vals.length - 1] || void 0 }),
450
+ disabled: readOnly
451
+ }
452
+ );
453
+ // ─── CONDITION REFERENCE ──────────────────────
454
+ case "condition":
455
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
456
+ editorProps.savedConditions?.length ? /* @__PURE__ */ jsxs("select", { className: inputClass, value: cond.conditionId || "", onChange: (e) => update({ conditionId: e.target.value }), disabled: readOnly, children: [
457
+ /* @__PURE__ */ jsx("option", { value: "", children: "Select a condition\u2026" }),
458
+ editorProps.savedConditions.map((c) => /* @__PURE__ */ jsx("option", { value: c.value, children: c.title }, c.value))
459
+ ] }) : /* @__PURE__ */ jsx("input", { className: inputClass, value: cond.conditionId || "", onChange: (e) => update({ conditionId: e.target.value }), placeholder: "Condition ID", disabled: readOnly }),
460
+ /* @__PURE__ */ jsx("div", { className: "flex gap-1 bg-gray-100 dark:bg-gray-800 rounded-md p-0.5 w-fit", children: [{ v: true, l: "Passes" }, { v: false, l: "Fails" }].map((opt) => /* @__PURE__ */ jsx(
461
+ "button",
462
+ {
463
+ onClick: () => update({ passes: opt.v }),
464
+ disabled: readOnly,
465
+ className: cn(
466
+ "px-2.5 py-1 text-xs font-medium rounded transition-colors",
467
+ cond.passes === opt.v ? "bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm" : "text-gray-500 dark:text-gray-400"
468
+ ),
469
+ children: opt.l
470
+ },
471
+ String(opt.v)
472
+ )) })
473
+ ] });
474
+ default:
475
+ return /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 italic", children: "Unknown condition type" });
476
+ }
477
+ };
478
+ var ConditionCard = ({
479
+ condition,
480
+ index,
481
+ onChange,
482
+ onDelete,
483
+ editorProps,
484
+ readOnly
485
+ }) => {
486
+ const [expanded, setExpanded] = useState(!condition.type);
487
+ const meta = getConditionMeta(condition.type);
488
+ const Icon = getConditionIcon(condition.type);
489
+ const summary = getConditionSummary(condition, { savedConditions: editorProps.savedConditions });
490
+ const handleSelectType = (type) => {
491
+ onChange({ ...condition, type });
492
+ setExpanded(true);
493
+ };
494
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden shadow-sm transition-shadow hover:shadow-md", children: [
495
+ /* @__PURE__ */ jsxs(
496
+ "div",
497
+ {
498
+ className: cn(
499
+ "flex items-center gap-3 px-4 py-3 cursor-pointer select-none transition-colors",
500
+ "hover:bg-gray-50 dark:hover:bg-gray-800/50"
501
+ ),
502
+ style: meta ? {
503
+ background: `linear-gradient(90deg, ${meta.color}08 0%, transparent 100%)`
504
+ } : void 0,
505
+ onClick: () => setExpanded(!expanded),
506
+ children: [
507
+ /* @__PURE__ */ jsx(
508
+ "div",
509
+ {
510
+ className: "w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0",
511
+ style: meta ? { backgroundColor: `${meta.color}18`, color: meta.color } : { backgroundColor: "#e5e7eb" },
512
+ children: /* @__PURE__ */ jsx(Icon, { className: "w-4 h-4" })
513
+ }
514
+ ),
515
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
516
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-800 dark:text-gray-200 truncate", children: meta?.title || "Select Condition Type" }),
517
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] text-gray-500 dark:text-gray-400 truncate", children: summary })
518
+ ] }),
519
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 flex-shrink-0", children: [
520
+ condition.type && !readOnly && /* @__PURE__ */ jsx(
521
+ "button",
522
+ {
523
+ onClick: (e) => {
524
+ e.stopPropagation();
525
+ onChange({ type: null });
526
+ },
527
+ className: "p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors",
528
+ title: "Change type",
529
+ children: /* @__PURE__ */ jsx(RefreshCw, { className: "w-3.5 h-3.5 text-gray-400" })
530
+ }
531
+ ),
532
+ !readOnly && /* @__PURE__ */ jsx(
533
+ "button",
534
+ {
535
+ onClick: (e) => {
536
+ e.stopPropagation();
537
+ onDelete();
538
+ },
539
+ className: "p-1 rounded hover:bg-red-100 dark:hover:bg-red-900/30 transition-colors",
540
+ title: "Delete",
541
+ children: /* @__PURE__ */ jsx(Trash2, { className: "w-3.5 h-3.5 text-red-400" })
542
+ }
543
+ ),
544
+ expanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "w-4 h-4 text-gray-400" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "w-4 h-4 text-gray-400" })
545
+ ] })
546
+ ]
547
+ }
548
+ ),
549
+ expanded && /* @__PURE__ */ jsx("div", { className: "border-t border-gray-100 dark:border-gray-800 px-4 py-3", children: !condition.type ? /* @__PURE__ */ jsx(ConditionTypePicker, { onSelect: handleSelectType }) : /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
550
+ meta?.helpText && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-gray-400 dark:text-gray-500 italic", children: meta.helpText }),
551
+ /* @__PURE__ */ jsx(
552
+ ConditionConfig,
553
+ {
554
+ condition,
555
+ onChange,
556
+ editorProps,
557
+ readOnly
558
+ }
559
+ )
560
+ ] }) })
561
+ ] });
562
+ };
563
+ var ConditionsSummary = ({
564
+ value,
565
+ savedConditions
566
+ }) => {
567
+ const count = value.conditions.filter((c) => c.type).length;
568
+ if (count === 0) {
569
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400", children: [
570
+ /* @__PURE__ */ jsx(Filter, { className: "w-4 h-4" }),
571
+ /* @__PURE__ */ jsx("span", { children: "No conditions set" }),
572
+ /* @__PURE__ */ jsx(ChevronRight, { className: "w-4 h-4 ml-auto" })
573
+ ] });
574
+ }
575
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
576
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs font-medium text-gray-500 dark:text-gray-400", children: [
577
+ /* @__PURE__ */ jsx(Filter, { className: "w-3.5 h-3.5" }),
578
+ count,
579
+ " condition",
580
+ count !== 1 ? "s" : "",
581
+ " \xB7 ",
582
+ value.type === "and" ? "Match ALL" : "Match ANY",
583
+ /* @__PURE__ */ jsx(ChevronRight, { className: "w-4 h-4 ml-auto" })
584
+ ] }),
585
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1.5", children: [
586
+ value.conditions.filter((c) => c.type).slice(0, 5).map((cond, i) => {
587
+ const meta = getConditionMeta(cond.type);
588
+ return /* @__PURE__ */ jsx(
589
+ "span",
590
+ {
591
+ className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] font-medium",
592
+ style: {
593
+ backgroundColor: meta ? `${meta.color}15` : "#e5e7eb",
594
+ color: meta?.color || "#6b7280"
595
+ },
596
+ children: getConditionSummary(cond, { savedConditions })
597
+ },
598
+ i
599
+ );
600
+ }),
601
+ count > 5 && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center px-2 py-0.5 rounded-full text-[11px] font-medium bg-gray-100 dark:bg-gray-800 text-gray-500", children: [
602
+ "+",
603
+ count - 5,
604
+ " more"
605
+ ] })
606
+ ] })
607
+ ] });
608
+ };
609
+ var EditorDialog = ({ open, onClose, children }) => {
610
+ if (!open) return null;
611
+ return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
612
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50 backdrop-blur-sm", onClick: onClose }),
613
+ /* @__PURE__ */ jsxs("div", { className: "relative z-10 w-full max-w-2xl max-h-[80vh] bg-white dark:bg-gray-900 rounded-xl shadow-2xl border border-gray-200 dark:border-gray-700 flex flex-col overflow-hidden mx-4", children: [
614
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700", children: [
615
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: "Edit Conditions" }),
616
+ /* @__PURE__ */ jsx(
617
+ "button",
618
+ {
619
+ onClick: onClose,
620
+ className: "p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors",
621
+ children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4 text-gray-500" })
622
+ }
623
+ )
624
+ ] }),
625
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto p-4", children })
626
+ ] })
627
+ ] });
628
+ };
629
+ var ConditionsEditorContent = ({
630
+ value,
631
+ onChange,
632
+ isGlobal,
633
+ versions,
634
+ tags,
635
+ products,
636
+ savedConditions,
637
+ userGroups,
638
+ readOnly = false,
639
+ className
640
+ }) => {
641
+ const editorProps = { versions, tags, products, savedConditions, userGroups };
642
+ const updateCondition = useCallback((index, updated) => {
643
+ const next = { ...value, conditions: [...value.conditions] };
644
+ next.conditions[index] = updated;
645
+ onChange(next);
646
+ }, [value, onChange]);
647
+ const deleteCondition = useCallback((index) => {
648
+ const next = { ...value, conditions: value.conditions.filter((_, i) => i !== index) };
649
+ onChange(next);
650
+ }, [value, onChange]);
651
+ const addCondition = useCallback(() => {
652
+ const next = { ...value, conditions: [...value.conditions, {}] };
653
+ onChange(next);
654
+ }, [value, onChange]);
655
+ const toggleLogic = useCallback((logic) => {
656
+ onChange({ ...value, type: logic });
657
+ }, [value, onChange]);
658
+ return /* @__PURE__ */ jsxs("div", { className: cn("smartlinks-ui-conditions-editor space-y-4", className), children: [
659
+ isGlobal && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-gray-200 dark:border-gray-700 p-4 space-y-3", children: [
660
+ /* @__PURE__ */ jsx(
661
+ "input",
662
+ {
663
+ className: "w-full px-3 py-2 text-sm rounded-md border border-gray-300 dark:border-gray-600 bg-transparent focus:outline-none focus:ring-1 focus:ring-blue-500",
664
+ value: value.title || "",
665
+ onChange: (e) => onChange({ ...value, title: e.target.value }),
666
+ placeholder: "Condition Title",
667
+ disabled: readOnly
668
+ }
669
+ ),
670
+ /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400 cursor-pointer", children: [
671
+ /* @__PURE__ */ jsx(
672
+ "button",
673
+ {
674
+ onClick: () => onChange({ ...value, disabled: !value.disabled }),
675
+ disabled: readOnly,
676
+ className: cn(
677
+ "w-9 h-5 rounded-full transition-colors relative",
678
+ value.disabled ? "bg-blue-500" : "bg-gray-300 dark:bg-gray-600"
679
+ ),
680
+ children: /* @__PURE__ */ jsx("span", { className: cn(
681
+ "absolute top-0.5 w-4 h-4 rounded-full bg-white shadow transition-transform",
682
+ value.disabled ? "translate-x-4" : "translate-x-0.5"
683
+ ) })
684
+ }
685
+ ),
686
+ "Disabled / Hidden"
687
+ ] })
688
+ ] }),
689
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-gray-200 dark:border-gray-700 p-4", children: [
690
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs font-medium text-gray-500 dark:text-gray-400 mb-3", children: [
691
+ /* @__PURE__ */ jsx(GitBranch, { className: "w-3.5 h-3.5" }),
692
+ "Logic Type"
693
+ ] }),
694
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1 bg-gray-100 dark:bg-gray-800 rounded-md p-0.5", children: [
695
+ /* @__PURE__ */ jsx(
696
+ "button",
697
+ {
698
+ onClick: () => toggleLogic("and"),
699
+ disabled: readOnly,
700
+ className: cn(
701
+ "flex-1 px-3 py-2 text-sm font-medium rounded transition-colors",
702
+ value.type === "and" ? "bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm" : "text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
703
+ ),
704
+ children: "Match ALL (AND)"
705
+ }
706
+ ),
707
+ /* @__PURE__ */ jsx(
708
+ "button",
709
+ {
710
+ onClick: () => toggleLogic("or"),
711
+ disabled: readOnly,
712
+ className: cn(
713
+ "flex-1 px-3 py-2 text-sm font-medium rounded transition-colors",
714
+ value.type === "or" ? "bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm" : "text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
715
+ ),
716
+ children: "Match ANY (OR)"
717
+ }
718
+ )
719
+ ] })
720
+ ] }),
721
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
722
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-gray-700 dark:text-gray-300", children: "Conditions" }),
723
+ /* @__PURE__ */ jsx("span", { className: "px-2 py-0.5 text-[10px] font-medium rounded-full bg-blue-100 dark:bg-blue-900/40 text-blue-700 dark:text-blue-300", children: value.conditions.length })
724
+ ] }),
725
+ /* @__PURE__ */ jsx("div", { className: "space-y-3", children: value.conditions.map((cond, idx) => /* @__PURE__ */ jsx(
726
+ ConditionCard,
727
+ {
728
+ condition: cond,
729
+ index: idx,
730
+ onChange: (updated) => updateCondition(idx, updated),
731
+ onDelete: () => deleteCondition(idx),
732
+ editorProps,
733
+ readOnly
734
+ },
735
+ idx
736
+ )) }),
737
+ !readOnly && /* @__PURE__ */ jsxs(
738
+ "button",
739
+ {
740
+ onClick: addCondition,
741
+ className: cn(
742
+ "w-full flex items-center justify-center gap-2 py-3 rounded-lg",
743
+ "border-2 border-dashed border-gray-300 dark:border-gray-600",
744
+ "text-sm text-gray-500 dark:text-gray-400",
745
+ "hover:border-blue-400 dark:hover:border-blue-500 hover:text-blue-500 dark:hover:text-blue-400",
746
+ "transition-colors cursor-pointer"
747
+ ),
748
+ children: [
749
+ /* @__PURE__ */ jsx(Plus, { className: "w-4 h-4" }),
750
+ "Add New Condition"
751
+ ]
752
+ }
753
+ )
754
+ ] });
755
+ };
756
+ var ConditionsEditor = (props) => {
757
+ const { mode = "inline", trigger, open: controlledOpen, onClose, value, savedConditions, className, ...rest } = props;
758
+ const [internalOpen, setInternalOpen] = useState(false);
759
+ const isOpen = controlledOpen ?? internalOpen;
760
+ const handleClose = useCallback(() => {
761
+ setInternalOpen(false);
762
+ onClose?.();
763
+ }, [onClose]);
764
+ if (mode === "inline") {
765
+ return /* @__PURE__ */ jsx(ConditionsEditorContent, { value, savedConditions, className, ...rest });
766
+ }
767
+ const triggerElement = trigger || /* @__PURE__ */ jsx("div", { className: cn(
768
+ "rounded-lg border border-gray-200 dark:border-gray-700 p-3 cursor-pointer",
769
+ "hover:border-gray-300 dark:hover:border-gray-600 transition-colors",
770
+ className
771
+ ), children: /* @__PURE__ */ jsx(ConditionsSummary, { value, savedConditions }) });
772
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
773
+ /* @__PURE__ */ jsx("span", { onClick: () => setInternalOpen(true), className: "cursor-pointer", children: triggerElement }),
774
+ /* @__PURE__ */ jsx(EditorDialog, { open: isOpen, onClose: handleClose, children: /* @__PURE__ */ jsx(ConditionsEditorContent, { value, savedConditions, ...rest }) })
775
+ ] });
776
+ };
777
+
778
+ export { ConditionsEditor };
779
+ //# sourceMappingURL=chunk-IVUFK6SS.js.map
780
+ //# sourceMappingURL=chunk-IVUFK6SS.js.map