@stamprally/admin-ui 0.9.0 → 0.10.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.
package/dist/index.cjs CHANGED
@@ -5,6 +5,9 @@ var react = require('react');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
 
7
7
  // src/index.tsx
8
+ function text(dictionary, locale, key, fallback) {
9
+ return dictionary?.[locale]?.[key] ?? fallback;
10
+ }
8
11
  var condition = () => ({ type: "passcode", code: "" });
9
12
  var newSpot = (index) => ({
10
13
  id: `spot-${index + 1}`,
@@ -19,21 +22,372 @@ var newReward = (index) => ({
19
22
  redemptionMethod: "server_claim",
20
23
  requiredStampCount: index + 1
21
24
  });
22
- function AdminRallyEditor({
23
- config,
25
+ function move(items, index, direction) {
26
+ const target = index + direction;
27
+ if (target < 0 || target >= items.length) return items;
28
+ const next = [...items];
29
+ const [item] = next.splice(index, 1);
30
+ if (item !== void 0) next.splice(target, 0, item);
31
+ return next;
32
+ }
33
+ function ConditionEditor({
34
+ condition: condition2,
35
+ onChange,
36
+ onRemove,
37
+ locale,
38
+ dictionary
39
+ }) {
40
+ const activeLocale = locale ?? "en";
41
+ const field = (key, fallback) => text(dictionary, activeLocale, key, fallback);
42
+ return /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { children: [
43
+ /* @__PURE__ */ jsxRuntime.jsx("legend", { children: field("condition", "Condition") }),
44
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
45
+ field("conditionType", "Type"),
46
+ /* @__PURE__ */ jsxRuntime.jsx(
47
+ "select",
48
+ {
49
+ value: condition2.type,
50
+ onChange: (event) => {
51
+ const type = event.target.value;
52
+ onChange(
53
+ type === "qr" ? { type, secretToken: "" } : type === "passcode" ? { type, code: "", caseSensitive: false } : type === "gps" ? { type, latitude: 0, longitude: 0, radiusMeters: 100 } : type === "nfc" ? { type, tagId: "" } : { type, validatorName: "" }
54
+ );
55
+ },
56
+ children: ["qr", "passcode", "gps", "nfc", "custom"].map((type) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: type, children: field(`condition.${type}`, type.toUpperCase()) }, type))
57
+ }
58
+ )
59
+ ] }),
60
+ condition2.type === "qr" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
61
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
62
+ field("secretToken", "QR token"),
63
+ /* @__PURE__ */ jsxRuntime.jsx(
64
+ "input",
65
+ {
66
+ value: condition2.secretToken,
67
+ onChange: (event) => onChange({ ...condition2, secretToken: event.target.value })
68
+ }
69
+ )
70
+ ] }),
71
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
72
+ field("qrEntryUrl", "QR entry URL"),
73
+ /* @__PURE__ */ jsxRuntime.jsx(
74
+ "input",
75
+ {
76
+ value: condition2.qrEntryUrl ?? "",
77
+ onChange: (event) => onChange({ ...condition2, qrEntryUrl: event.target.value })
78
+ }
79
+ )
80
+ ] })
81
+ ] }),
82
+ condition2.type === "passcode" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
83
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
84
+ field("passcode", "Passcode"),
85
+ /* @__PURE__ */ jsxRuntime.jsx(
86
+ "input",
87
+ {
88
+ value: condition2.code,
89
+ onChange: (event) => onChange({ ...condition2, code: event.target.value })
90
+ }
91
+ )
92
+ ] }),
93
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
94
+ /* @__PURE__ */ jsxRuntime.jsx(
95
+ "input",
96
+ {
97
+ type: "checkbox",
98
+ checked: condition2.caseSensitive ?? false,
99
+ onChange: (event) => onChange({ ...condition2, caseSensitive: event.target.checked })
100
+ }
101
+ ),
102
+ field("caseSensitive", "Case sensitive")
103
+ ] })
104
+ ] }),
105
+ condition2.type === "gps" && /* @__PURE__ */ jsxRuntime.jsx("div", { children: ["latitude", "longitude", "radiusMeters"].map((key) => /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
106
+ field(key, key),
107
+ /* @__PURE__ */ jsxRuntime.jsx(
108
+ "input",
109
+ {
110
+ type: "number",
111
+ value: condition2[key],
112
+ onChange: (event) => onChange({ ...condition2, [key]: Number(event.target.value) })
113
+ }
114
+ )
115
+ ] }, key)) }),
116
+ condition2.type === "nfc" && /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
117
+ field("tagId", "NFC tag ID"),
118
+ /* @__PURE__ */ jsxRuntime.jsx(
119
+ "input",
120
+ {
121
+ value: condition2.tagId,
122
+ onChange: (event) => onChange({ ...condition2, tagId: event.target.value })
123
+ }
124
+ )
125
+ ] }),
126
+ condition2.type === "custom" && /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
127
+ field("validatorName", "Validator name"),
128
+ /* @__PURE__ */ jsxRuntime.jsx(
129
+ "input",
130
+ {
131
+ value: condition2.validatorName,
132
+ onChange: (event) => onChange({ ...condition2, validatorName: event.target.value })
133
+ }
134
+ )
135
+ ] }),
136
+ onRemove !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onRemove, children: field("removeCondition", "Remove condition") })
137
+ ] });
138
+ }
139
+ function SpotItemForm({
140
+ spot,
141
+ onChange,
142
+ onRemove,
143
+ locale,
144
+ dictionary
145
+ }) {
146
+ const activeLocale = locale ?? "en";
147
+ const field = (key, fallback) => text(dictionary, activeLocale, key, fallback);
148
+ const update = (patch) => onChange({ ...spot, ...patch });
149
+ return /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { children: [
150
+ /* @__PURE__ */ jsxRuntime.jsx("legend", { children: core.resolveLocalizedText(spot.name, activeLocale) }),
151
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
152
+ field("spotName", "Spot name"),
153
+ /* @__PURE__ */ jsxRuntime.jsx(
154
+ "input",
155
+ {
156
+ value: core.resolveLocalizedText(spot.name, activeLocale),
157
+ onChange: (event) => update({ name: core.updateLocalizedField(spot.name, activeLocale, event.target.value) })
158
+ }
159
+ )
160
+ ] }),
161
+ ["imageUrl", "iconUrl", "redirectUrlAfterClaim"].map((key) => /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
162
+ field(key, key),
163
+ /* @__PURE__ */ jsxRuntime.jsx(
164
+ "input",
165
+ {
166
+ value: spot[key] ?? "",
167
+ onChange: (event) => update({ [key]: event.target.value })
168
+ }
169
+ )
170
+ ] }, key)),
171
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
172
+ field("prerequisites", "Prerequisite spots"),
173
+ /* @__PURE__ */ jsxRuntime.jsx(
174
+ "input",
175
+ {
176
+ value: spot.prerequisites?.join(", ") ?? "",
177
+ onChange: (event) => update({
178
+ prerequisites: event.target.value.split(",").map((item) => item.trim()).filter(Boolean)
179
+ })
180
+ }
181
+ )
182
+ ] }),
183
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
184
+ field("externalReferences", "External references"),
185
+ /* @__PURE__ */ jsxRuntime.jsx(
186
+ "textarea",
187
+ {
188
+ value: JSON.stringify(spot.externalReferences ?? [], null, 2),
189
+ onChange: (event) => {
190
+ try {
191
+ const parsed = JSON.parse(event.target.value);
192
+ if (Array.isArray(parsed)) update({ externalReferences: parsed });
193
+ } catch {
194
+ }
195
+ }
196
+ }
197
+ )
198
+ ] }),
199
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
200
+ field("metadata", "Metadata"),
201
+ /* @__PURE__ */ jsxRuntime.jsx(
202
+ "textarea",
203
+ {
204
+ value: JSON.stringify(spot.metadata ?? {}, null, 2),
205
+ onChange: (event) => {
206
+ try {
207
+ const parsed = JSON.parse(event.target.value);
208
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed))
209
+ update({ metadata: parsed });
210
+ } catch {
211
+ }
212
+ }
213
+ }
214
+ )
215
+ ] }),
216
+ spot.conditions.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
217
+ ConditionEditor,
218
+ {
219
+ condition: item,
220
+ locale: activeLocale,
221
+ ...dictionary === void 0 ? {} : { dictionary },
222
+ onChange: (next) => update({
223
+ conditions: spot.conditions.map(
224
+ (current, itemIndex) => itemIndex === index ? next : current
225
+ )
226
+ }),
227
+ ...spot.conditions.length <= 1 ? {} : {
228
+ onRemove: () => update({
229
+ conditions: spot.conditions.filter((_, itemIndex) => itemIndex !== index)
230
+ })
231
+ }
232
+ },
233
+ `${spot.id}-condition-${JSON.stringify(item)}`
234
+ )),
235
+ /* @__PURE__ */ jsxRuntime.jsx(
236
+ "button",
237
+ {
238
+ type: "button",
239
+ onClick: () => update({ conditions: [...spot.conditions, condition()] }),
240
+ children: field("addCondition", "Add condition")
241
+ }
242
+ ),
243
+ onRemove !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onRemove, children: field("removeSpot", "Remove spot") })
244
+ ] });
245
+ }
246
+ function RewardItemForm({
247
+ reward,
248
+ locale,
249
+ dictionary,
24
250
  onChange,
25
- locale = "en"
251
+ onRemove
26
252
  }) {
253
+ const field = (key, fallback) => text(dictionary, locale, key, fallback);
254
+ return /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { children: [
255
+ /* @__PURE__ */ jsxRuntime.jsx("legend", { children: core.resolveLocalizedText(reward.title, locale) }),
256
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
257
+ field("rewardTitle", "Reward title"),
258
+ /* @__PURE__ */ jsxRuntime.jsx(
259
+ "input",
260
+ {
261
+ value: core.resolveLocalizedText(reward.title, locale),
262
+ onChange: (event) => onChange({
263
+ ...reward,
264
+ title: core.updateLocalizedField(reward.title, locale, event.target.value)
265
+ })
266
+ }
267
+ )
268
+ ] }),
269
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
270
+ field("requiredSpotCount", "Required spot count"),
271
+ /* @__PURE__ */ jsxRuntime.jsx(
272
+ "input",
273
+ {
274
+ type: "number",
275
+ min: 0,
276
+ value: reward.requiredStampCount,
277
+ onChange: (event) => onChange({ ...reward, requiredStampCount: Number(event.target.value) })
278
+ }
279
+ )
280
+ ] }),
281
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
282
+ field("rewardType", "Reward type"),
283
+ /* @__PURE__ */ jsxRuntime.jsxs(
284
+ "select",
285
+ {
286
+ value: reward.type,
287
+ onChange: (event) => onChange({ ...reward, type: event.target.value }),
288
+ children: [
289
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "digital", children: field("rewardType.digital", "Digital") }),
290
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "in_person", children: field("rewardType.inPerson", "In person") })
291
+ ]
292
+ }
293
+ )
294
+ ] }),
295
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
296
+ field("redemptionMethod", "Redemption method"),
297
+ /* @__PURE__ */ jsxRuntime.jsxs(
298
+ "select",
299
+ {
300
+ value: reward.redemptionMethod,
301
+ onChange: (event) => onChange({
302
+ ...reward,
303
+ redemptionMethod: event.target.value
304
+ }),
305
+ children: [
306
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "server_claim", children: field("redemption.serverClaim", "Server claim") }),
307
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "manual_slide", children: field("redemption.manualSlide", "Manual slide") }),
308
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "staff_passcode", children: field("redemption.staffPasscode", "Staff passcode") }),
309
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "view_only", children: field("redemption.viewOnly", "View only") })
310
+ ]
311
+ }
312
+ )
313
+ ] }),
314
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
315
+ field("unlockConditions", "Unlock conditions"),
316
+ /* @__PURE__ */ jsxRuntime.jsx(
317
+ "textarea",
318
+ {
319
+ value: JSON.stringify(reward.conditions ?? [], null, 2),
320
+ onChange: (event) => {
321
+ try {
322
+ const parsed = JSON.parse(event.target.value);
323
+ if (Array.isArray(parsed))
324
+ onChange({ ...reward, conditions: parsed });
325
+ } catch {
326
+ }
327
+ }
328
+ }
329
+ )
330
+ ] }),
331
+ ["stockLimit", "userClaimLimit"].map((key) => /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
332
+ field(key, key),
333
+ /* @__PURE__ */ jsxRuntime.jsx(
334
+ "input",
335
+ {
336
+ type: "number",
337
+ min: 0,
338
+ value: reward[key] ?? "",
339
+ onChange: (event) => {
340
+ if (event.target.value === "") {
341
+ const { [key]: _removed, ...withoutLimit } = reward;
342
+ onChange(withoutLimit);
343
+ } else onChange({ ...reward, [key]: Number(event.target.value) });
344
+ }
345
+ }
346
+ )
347
+ ] }, key)),
348
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
349
+ field("staffPasscode", "Staff passcode"),
350
+ /* @__PURE__ */ jsxRuntime.jsx(
351
+ "input",
352
+ {
353
+ value: reward.staffPasscode ?? "",
354
+ onChange: (event) => onChange({ ...reward, staffPasscode: event.target.value })
355
+ }
356
+ )
357
+ ] }),
358
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
359
+ field("validUntil", "Valid until"),
360
+ /* @__PURE__ */ jsxRuntime.jsx(
361
+ "input",
362
+ {
363
+ type: "datetime-local",
364
+ value: reward.validUntil?.slice(0, 16) ?? "",
365
+ onChange: (event) => {
366
+ if (event.target.value === "") {
367
+ const { validUntil: _removed, ...withoutDate } = reward;
368
+ onChange(withoutDate);
369
+ } else onChange({ ...reward, validUntil: new Date(event.target.value).toISOString() });
370
+ }
371
+ }
372
+ )
373
+ ] }),
374
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onRemove, children: field("removeReward", "Remove reward") })
375
+ ] });
376
+ }
377
+ function AdminRallyEditor({ config, onChange, locale, dictionary }) {
378
+ const activeLocale = locale ?? "en";
379
+ const field = (key, fallback) => text(dictionary, activeLocale, key, fallback);
27
380
  const update = (next) => onChange({ ...config, ...next });
28
- return /* @__PURE__ */ jsxRuntime.jsxs("section", { "aria-label": "Rally editor", children: [
29
- /* @__PURE__ */ jsxRuntime.jsx("h1", { children: core.resolveLocalizedText(config.title, locale) }),
381
+ const updateSpots = (spots) => update({ spots: spots.map((spot, index) => ({ ...spot, orderIndex: index })) });
382
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { "aria-label": field("rallyEditor", "Rally editor"), children: [
383
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { children: core.resolveLocalizedText(config.title, activeLocale) }),
30
384
  /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
31
- "Title",
385
+ field("title", "Title"),
32
386
  /* @__PURE__ */ jsxRuntime.jsx(
33
387
  "input",
34
388
  {
35
- value: core.resolveLocalizedText(config.title, locale),
36
- onChange: (event) => update({ title: event.target.value })
389
+ value: core.resolveLocalizedText(config.title, activeLocale),
390
+ onChange: (event) => update({ title: core.updateLocalizedField(config.title, activeLocale, event.target.value) })
37
391
  }
38
392
  )
39
393
  ] }),
@@ -41,73 +395,136 @@ function AdminRallyEditor({
41
395
  "button",
42
396
  {
43
397
  type: "button",
44
- onClick: () => update({ spots: [...config.spots, newSpot(config.spots.length)] }),
45
- children: "Add spot"
398
+ onClick: () => updateSpots([...config.spots, newSpot(config.spots.length)]),
399
+ children: field("addSpot", "Add spot")
46
400
  }
47
401
  ),
402
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: config.spots.map((spot, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
403
+ /* @__PURE__ */ jsxRuntime.jsx(
404
+ SpotItemForm,
405
+ {
406
+ spot,
407
+ locale: activeLocale,
408
+ ...dictionary === void 0 ? {} : { dictionary },
409
+ onChange: (next) => updateSpots(
410
+ config.spots.map((current) => current.id === spot.id ? next : current)
411
+ ),
412
+ onRemove: () => updateSpots(config.spots.filter((current) => current.id !== spot.id))
413
+ }
414
+ ),
415
+ /* @__PURE__ */ jsxRuntime.jsx(
416
+ "button",
417
+ {
418
+ type: "button",
419
+ disabled: index === 0,
420
+ onClick: () => updateSpots(move(config.spots, index, -1)),
421
+ children: field("moveUp", "Move up")
422
+ }
423
+ ),
424
+ /* @__PURE__ */ jsxRuntime.jsx(
425
+ "button",
426
+ {
427
+ type: "button",
428
+ disabled: index === config.spots.length - 1,
429
+ onClick: () => updateSpots(move(config.spots, index, 1)),
430
+ children: field("moveDown", "Move down")
431
+ }
432
+ )
433
+ ] }, spot.id)) }),
48
434
  /* @__PURE__ */ jsxRuntime.jsx(
49
435
  "button",
50
436
  {
51
437
  type: "button",
52
438
  onClick: () => update({ rewards: [...config.rewards, newReward(config.rewards.length)] }),
53
- children: "Add reward"
439
+ children: field("addReward", "Add reward")
54
440
  }
55
441
  ),
56
- /* @__PURE__ */ jsxRuntime.jsx("ul", { children: config.spots.map((spot) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: core.resolveLocalizedText(spot.name, locale) }, spot.id)) })
442
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: config.rewards.map((reward) => /* @__PURE__ */ jsxRuntime.jsx(
443
+ RewardItemForm,
444
+ {
445
+ reward,
446
+ locale: activeLocale,
447
+ ...dictionary === void 0 ? {} : { dictionary },
448
+ onChange: (next) => update({
449
+ rewards: config.rewards.map(
450
+ (current) => current.id === reward.id ? next : current
451
+ )
452
+ }),
453
+ onRemove: () => update({ rewards: config.rewards.filter((current) => current.id !== reward.id) })
454
+ },
455
+ reward.id
456
+ )) })
57
457
  ] });
58
458
  }
59
459
  var RallyEditor = AdminRallyEditor;
60
- function SpotItemForm({
61
- spot,
62
- onChange
460
+ function GeneralSettingsForm({
461
+ config,
462
+ onChange,
463
+ locale,
464
+ dictionary
63
465
  }) {
466
+ const activeLocale = locale ?? "en";
64
467
  return /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
65
- "Spot name",
468
+ text(dictionary, activeLocale, "title", "Rally title"),
66
469
  /* @__PURE__ */ jsxRuntime.jsx(
67
470
  "input",
68
471
  {
69
- value: core.resolveLocalizedText(spot.name, "en"),
70
- onChange: (event) => onChange({ ...spot, name: event.target.value })
472
+ value: core.resolveLocalizedText(config.title, activeLocale),
473
+ onChange: (event) => onChange({
474
+ ...config,
475
+ title: core.updateLocalizedField(config.title, activeLocale, event.target.value)
476
+ })
71
477
  }
72
478
  )
73
479
  ] });
74
480
  }
75
- function GeneralSettingsForm({ config, onChange }) {
76
- return /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
77
- "Rally title",
481
+ function JsonConfigIO({ config, onImport, locale, dictionary }) {
482
+ const activeLocale = locale ?? "en";
483
+ const [value, setValue] = react.useState(() => JSON.stringify(config, null, 2));
484
+ const [errors, setErrors] = react.useState([]);
485
+ const field = (key, fallback) => text(dictionary, activeLocale, key, fallback);
486
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
78
487
  /* @__PURE__ */ jsxRuntime.jsx(
79
- "input",
488
+ "textarea",
80
489
  {
81
- value: core.resolveLocalizedText(config.title, "en"),
82
- onChange: (event) => onChange({ ...config, title: event.target.value })
490
+ "aria-label": field("jsonConfig", "JSON configuration"),
491
+ value,
492
+ onChange: (event) => {
493
+ setValue(event.target.value);
494
+ setErrors([]);
495
+ }
83
496
  }
84
- )
85
- ] });
86
- }
87
- function JsonConfigIO({ config, onImport }) {
88
- const [value, setValue] = react.useState(() => JSON.stringify(config, null, 2));
89
- const parse = (event) => {
90
- setValue(event.target.value);
91
- };
92
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
93
- /* @__PURE__ */ jsxRuntime.jsx("textarea", { value, onChange: parse }),
497
+ ),
94
498
  /* @__PURE__ */ jsxRuntime.jsx(
95
499
  "button",
96
500
  {
97
501
  type: "button",
98
502
  onClick: () => {
99
503
  try {
100
- onImport(JSON.parse(value));
504
+ const result = core.safeParseAdminConfig(JSON.parse(value));
505
+ if (!result.success) {
506
+ setErrors(result.errors);
507
+ return;
508
+ }
509
+ onImport(result.data);
510
+ setErrors([]);
101
511
  } catch {
512
+ setErrors([{ path: "$", message: field("invalidJson", "Invalid JSON.") }]);
102
513
  }
103
514
  },
104
- children: "Import"
515
+ children: field("import", "Import")
105
516
  }
106
- )
517
+ ),
518
+ errors.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("ul", { role: "alert", children: errors.map((error) => /* @__PURE__ */ jsxRuntime.jsxs("li", { children: [
519
+ error.path,
520
+ ": ",
521
+ error.message
522
+ ] }, `${error.path}-${error.message}`)) })
107
523
  ] });
108
524
  }
109
525
 
110
526
  exports.AdminRallyEditor = AdminRallyEditor;
527
+ exports.ConditionEditor = ConditionEditor;
111
528
  exports.GeneralSettingsForm = GeneralSettingsForm;
112
529
  exports.JsonConfigIO = JsonConfigIO;
113
530
  exports.RallyEditor = RallyEditor;