sa2kit 1.6.69 → 1.6.72

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.
Files changed (43) hide show
  1. package/dist/festivalCard/core/index.d.mts +1 -1
  2. package/dist/festivalCard/core/index.d.ts +1 -1
  3. package/dist/festivalCard/core/index.js +4 -2
  4. package/dist/festivalCard/core/index.js.map +1 -1
  5. package/dist/festivalCard/core/index.mjs +4 -2
  6. package/dist/festivalCard/core/index.mjs.map +1 -1
  7. package/dist/festivalCard/index.d.mts +3 -3
  8. package/dist/festivalCard/index.d.ts +3 -3
  9. package/dist/festivalCard/index.js +220 -119
  10. package/dist/festivalCard/index.js.map +1 -1
  11. package/dist/festivalCard/index.mjs +219 -120
  12. package/dist/festivalCard/index.mjs.map +1 -1
  13. package/dist/festivalCard/miniapp/index.d.mts +3 -3
  14. package/dist/festivalCard/miniapp/index.d.ts +3 -3
  15. package/dist/festivalCard/miniapp/index.js +51 -15
  16. package/dist/festivalCard/miniapp/index.js.map +1 -1
  17. package/dist/festivalCard/miniapp/index.mjs +51 -15
  18. package/dist/festivalCard/miniapp/index.mjs.map +1 -1
  19. package/dist/festivalCard/routes/index.d.mts +44 -0
  20. package/dist/festivalCard/routes/index.d.ts +44 -0
  21. package/dist/festivalCard/routes/index.js +268 -0
  22. package/dist/festivalCard/routes/index.js.map +1 -0
  23. package/dist/festivalCard/routes/index.mjs +263 -0
  24. package/dist/festivalCard/routes/index.mjs.map +1 -0
  25. package/dist/festivalCard/server/index.d.mts +1 -1
  26. package/dist/festivalCard/server/index.d.ts +1 -1
  27. package/dist/festivalCard/web/index.d.mts +18 -4
  28. package/dist/festivalCard/web/index.d.ts +18 -4
  29. package/dist/festivalCard/web/index.js +220 -119
  30. package/dist/festivalCard/web/index.js.map +1 -1
  31. package/dist/festivalCard/web/index.mjs +219 -120
  32. package/dist/festivalCard/web/index.mjs.map +1 -1
  33. package/dist/{festivalCardService-uSg0oNuD.d.mts → festivalCardService-CqBTVC6x.d.mts} +3 -1
  34. package/dist/{festivalCardService-C0fhTezx.d.ts → festivalCardService-iFKu0k9q.d.ts} +3 -1
  35. package/dist/index.d.mts +3 -3
  36. package/dist/index.d.ts +3 -3
  37. package/dist/index.js +220 -119
  38. package/dist/index.js.map +1 -1
  39. package/dist/index.mjs +219 -120
  40. package/dist/index.mjs.map +1 -1
  41. package/dist/{types-B-tjQTGi.d.mts → types-D2WetAPc.d.mts} +8 -1
  42. package/dist/{types-B-tjQTGi.d.ts → types-D2WetAPc.d.ts} +8 -1
  43. package/package.json +6 -1
@@ -55,7 +55,8 @@ var DEFAULT_FESTIVAL_CARD_CONFIG = {
55
55
  src: "https://images.unsplash.com/photo-1512389142860-9c449e58a543?auto=format&fit=crop&w=1200&q=80",
56
56
  fit: "cover",
57
57
  borderRadius: 16,
58
- alt: "holiday"
58
+ alt: "holiday",
59
+ isBackground: false
59
60
  }
60
61
  ]
61
62
  },
@@ -114,7 +115,8 @@ var DEFAULT_FESTIVAL_CARD_CONFIG = {
114
115
  src: "https://images.unsplash.com/photo-1456324504439-367cee3b3c32?auto=format&fit=crop&w=1200&q=80",
115
116
  fit: "cover",
116
117
  borderRadius: 14,
117
- alt: "gift"
118
+ alt: "gift",
119
+ isBackground: false
118
120
  },
119
121
  {
120
122
  id: "p3-text-1",
@@ -180,9 +182,10 @@ var useFestivalCardConfig = (options) => {
180
182
  const [loading, setLoading] = useState(Boolean(options?.fetchConfig));
181
183
  const [saving, setSaving] = useState(false);
182
184
  useEffect(() => {
183
- if (!options?.fetchConfig) return;
185
+ const fetchConfig = options?.fetchConfig;
186
+ if (!fetchConfig) return;
184
187
  let active = true;
185
- void options.fetchConfig().then((value) => {
188
+ void fetchConfig().then((value) => {
186
189
  if (!active) return;
187
190
  setConfig(normalizeFestivalCardConfig(value));
188
191
  }).finally(() => {
@@ -191,16 +194,17 @@ var useFestivalCardConfig = (options) => {
191
194
  return () => {
192
195
  active = false;
193
196
  };
194
- }, [options]);
197
+ }, [options?.fetchConfig]);
195
198
  const save = useCallback(async () => {
196
- if (!options?.onSave) return;
199
+ const onSave = options?.onSave;
200
+ if (!onSave) return;
197
201
  setSaving(true);
198
202
  try {
199
- await options.onSave(config);
203
+ await onSave(config);
200
204
  } finally {
201
205
  setSaving(false);
202
206
  }
203
- }, [config, options]);
207
+ }, [config, options?.onSave]);
204
208
  return useMemo(
205
209
  () => ({
206
210
  config,
@@ -214,6 +218,7 @@ var useFestivalCardConfig = (options) => {
214
218
  };
215
219
  var elementStyle = (element) => ({
216
220
  position: "absolute",
221
+ zIndex: 2,
217
222
  left: `${element.x}%`,
218
223
  top: `${element.y}%`,
219
224
  width: `${element.width ?? 70}%`,
@@ -256,22 +261,23 @@ var renderElement = (element) => {
256
261
  );
257
262
  };
258
263
  var FestivalCardPageRenderer = ({ page }) => {
264
+ const backgroundElement = page.elements.find(
265
+ (element) => element.type === "image" && Boolean(element.isBackground)
266
+ );
267
+ const foregroundElements = page.elements.filter((element) => !(element.type === "image" && element.isBackground));
259
268
  return /* @__PURE__ */ React3.createElement(
260
269
  "div",
261
270
  {
271
+ className: "relative h-full w-full overflow-hidden rounded-2xl",
262
272
  style: {
263
- position: "relative",
264
- width: "100%",
265
- height: "100%",
266
- overflow: "hidden",
267
- borderRadius: 16,
268
273
  backgroundColor: page.background?.color || "#0f172a",
269
- backgroundImage: page.background?.image ? `url(${page.background.image})` : void 0,
274
+ backgroundImage: backgroundElement ? `url(${backgroundElement.src})` : page.background?.image ? `url(${page.background.image})` : void 0,
270
275
  backgroundSize: "cover",
271
276
  backgroundPosition: "center"
272
277
  }
273
278
  },
274
- page.elements.map(renderElement)
279
+ /* @__PURE__ */ React3.createElement("div", { className: "absolute inset-0 bg-slate-950/20" }),
280
+ foregroundElements.map(renderElement)
275
281
  );
276
282
  };
277
283
 
@@ -285,100 +291,31 @@ var FestivalCardBook3D = ({ config, className }) => {
285
291
  return /* @__PURE__ */ React3.createElement("div", { className }, /* @__PURE__ */ React3.createElement(
286
292
  "div",
287
293
  {
294
+ className: "w-full min-h-[560px] rounded-3xl p-6 shadow-2xl shadow-slate-900/50",
288
295
  style: {
289
- width: "100%",
290
- minHeight: 560,
291
- borderRadius: 24,
292
- padding: 24,
293
- background: `linear-gradient(145deg, ${normalized.background?.colorA || "#0c1a34"} 0%, ${normalized.background?.colorB || "#1f4f8a"} 100%)`,
294
- boxShadow: "0 26px 70px rgba(2, 6, 23, 0.45)"
296
+ background: `linear-gradient(145deg, ${normalized.background?.colorA || "#0c1a34"} 0%, ${normalized.background?.colorB || "#1f4f8a"} 100%)`
295
297
  }
296
298
  },
297
- /* @__PURE__ */ React3.createElement(
298
- "div",
299
- {
300
- style: {
301
- marginBottom: 14,
302
- color: "#f8fafc",
303
- fontSize: 14,
304
- opacity: 0.9,
305
- textAlign: "center"
306
- }
307
- },
308
- normalized.coverTitle || "Festival Card",
309
- " \xB7 \u7B2C ",
310
- currentPage + 1,
311
- " / ",
312
- pages.length,
313
- " \u9875"
314
- ),
315
- /* @__PURE__ */ React3.createElement("div", { style: { perspective: 1400, width: "100%", maxWidth: 920, margin: "0 auto" } }, /* @__PURE__ */ React3.createElement(
299
+ /* @__PURE__ */ React3.createElement("div", { className: "mx-auto w-full max-w-5xl text-center text-slate-100" }, /* @__PURE__ */ React3.createElement("h3", { className: "mb-4 text-lg font-semibold" }, normalized.coverTitle || "Festival Card")),
300
+ /* @__PURE__ */ React3.createElement("div", { className: "mx-auto w-full max-w-5xl" }, /* @__PURE__ */ React3.createElement("div", { className: "relative h-[460px]" }, pages.map((page, index) => /* @__PURE__ */ React3.createElement(
316
301
  "div",
317
302
  {
303
+ key: page.id,
304
+ className: "absolute inset-0 transition-opacity duration-500 ease-out",
318
305
  style: {
319
- position: "relative",
320
- height: 460,
321
- transformStyle: "preserve-3d"
306
+ opacity: index === currentPage ? 1 : 0,
307
+ pointerEvents: index === currentPage ? "auto" : "none"
322
308
  }
323
309
  },
324
- pages.map((page, index) => {
325
- const isFlipped = index < currentPage;
326
- const zIndex = pages.length - index;
327
- return /* @__PURE__ */ React3.createElement(
328
- "div",
329
- {
330
- key: page.id,
331
- style: {
332
- position: "absolute",
333
- inset: 0,
334
- transformStyle: "preserve-3d",
335
- transformOrigin: "left center",
336
- transform: `rotateY(${isFlipped ? -170 : 0}deg)`,
337
- transition: "transform 600ms cubic-bezier(0.2, 0.8, 0.2, 1)",
338
- zIndex
339
- }
340
- },
341
- /* @__PURE__ */ React3.createElement(
342
- "div",
343
- {
344
- style: {
345
- position: "absolute",
346
- inset: 0,
347
- backfaceVisibility: "hidden"
348
- }
349
- },
350
- /* @__PURE__ */ React3.createElement(FestivalCardPageRenderer, { page })
351
- ),
352
- /* @__PURE__ */ React3.createElement(
353
- "div",
354
- {
355
- style: {
356
- position: "absolute",
357
- inset: 0,
358
- transform: "rotateY(180deg)",
359
- backfaceVisibility: "hidden",
360
- borderRadius: 16,
361
- background: "#0f172a"
362
- }
363
- }
364
- )
365
- );
366
- })
367
- )),
368
- /* @__PURE__ */ React3.createElement("div", { style: { display: "flex", justifyContent: "center", gap: 12, marginTop: 18 } }, /* @__PURE__ */ React3.createElement(
310
+ /* @__PURE__ */ React3.createElement(FestivalCardPageRenderer, { page })
311
+ )))),
312
+ /* @__PURE__ */ React3.createElement("div", { className: "mt-5 flex justify-center gap-3" }, /* @__PURE__ */ React3.createElement(
369
313
  "button",
370
314
  {
371
315
  type: "button",
372
316
  disabled: !canPrev,
373
317
  onClick: () => setCurrentPage((p) => Math.max(0, p - 1)),
374
- style: {
375
- border: "none",
376
- borderRadius: 999,
377
- padding: "9px 16px",
378
- fontSize: 14,
379
- cursor: canPrev ? "pointer" : "not-allowed",
380
- opacity: canPrev ? 1 : 0.4
381
- }
318
+ className: "rounded-full bg-white px-5 py-2 text-sm font-medium text-slate-900 disabled:cursor-not-allowed disabled:opacity-45"
382
319
  },
383
320
  "\u4E0A\u4E00\u9875"
384
321
  ), /* @__PURE__ */ React3.createElement(
@@ -387,14 +324,7 @@ var FestivalCardBook3D = ({ config, className }) => {
387
324
  type: "button",
388
325
  disabled: !canNext,
389
326
  onClick: () => setCurrentPage((p) => Math.min(pages.length - 1, p + 1)),
390
- style: {
391
- border: "none",
392
- borderRadius: 999,
393
- padding: "9px 16px",
394
- fontSize: 14,
395
- cursor: canNext ? "pointer" : "not-allowed",
396
- opacity: canNext ? 1 : 0.4
397
- }
327
+ className: "rounded-full bg-sky-300 px-5 py-2 text-sm font-medium text-slate-900 disabled:cursor-not-allowed disabled:opacity-45"
398
328
  },
399
329
  "\u4E0B\u4E00\u9875"
400
330
  ))
@@ -405,7 +335,7 @@ var FestivalCardBook3D = ({ config, className }) => {
405
335
  autoPlay: normalized.backgroundMusic.autoPlay,
406
336
  loop: normalized.backgroundMusic.loop,
407
337
  controls: true,
408
- style: { width: "100%", marginTop: 10 }
338
+ className: "mt-3 w-full"
409
339
  }
410
340
  ) : null);
411
341
  };
@@ -447,16 +377,17 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
447
377
  )
448
378
  });
449
379
  };
450
- return /* @__PURE__ */ React3.createElement("div", { style: { borderRadius: 16, background: "#0f172a", color: "#e2e8f0", padding: 16 } }, /* @__PURE__ */ React3.createElement("div", { style: { display: "grid", gap: 12 } }, /* @__PURE__ */ React3.createElement("label", { style: { display: "grid", gap: 6 } }, /* @__PURE__ */ React3.createElement("span", null, "\u9875\u9762\u6570\u91CF"), /* @__PURE__ */ React3.createElement(
380
+ return /* @__PURE__ */ React3.createElement("div", { className: "rounded-2xl border border-slate-200 bg-white p-4 text-slate-900 shadow-sm" }, /* @__PURE__ */ React3.createElement("div", { className: "grid gap-3" }, /* @__PURE__ */ React3.createElement("label", { className: "grid gap-1.5" }, /* @__PURE__ */ React3.createElement("span", { className: "text-sm font-medium text-slate-700" }, "\u9875\u9762\u6570\u91CF"), /* @__PURE__ */ React3.createElement(
451
381
  "input",
452
382
  {
453
383
  type: "number",
454
384
  min: 1,
455
385
  max: 12,
456
386
  value: value.pages.length,
457
- onChange: (event) => onChange(resizeFestivalCardPages(value, Number(event.target.value)))
387
+ onChange: (event) => onChange(resizeFestivalCardPages(value, Number(event.target.value))),
388
+ className: "rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
458
389
  }
459
- )), /* @__PURE__ */ React3.createElement("label", { style: { display: "grid", gap: 6 } }, /* @__PURE__ */ React3.createElement("span", null, "\u80CC\u666F\u97F3\u4E50 URL"), /* @__PURE__ */ React3.createElement(
390
+ )), /* @__PURE__ */ React3.createElement("label", { className: "grid gap-1.5" }, /* @__PURE__ */ React3.createElement("span", { className: "text-sm font-medium text-slate-700" }, "\u80CC\u666F\u97F3\u4E50 URL"), /* @__PURE__ */ React3.createElement(
460
391
  "input",
461
392
  {
462
393
  type: "url",
@@ -467,9 +398,18 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
467
398
  ...value.backgroundMusic,
468
399
  src: event.target.value
469
400
  }
470
- })
401
+ }),
402
+ className: "rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
471
403
  }
472
- )), /* @__PURE__ */ React3.createElement("label", { style: { display: "grid", gap: 6 } }, /* @__PURE__ */ React3.createElement("span", null, "\u7F16\u8F91\u9875\u9762"), /* @__PURE__ */ React3.createElement("select", { value: activePageIndex, onChange: (event) => setActivePageIndex(Number(event.target.value)) }, pageOptions.map((index) => /* @__PURE__ */ React3.createElement("option", { key: index, value: index }, "\u7B2C ", index + 1, " \u9875"))))), canEditPage ? /* @__PURE__ */ React3.createElement("div", { style: { marginTop: 16 } }, /* @__PURE__ */ React3.createElement("div", { style: { display: "flex", gap: 8, marginBottom: 12 } }, /* @__PURE__ */ React3.createElement(
404
+ )), /* @__PURE__ */ React3.createElement("label", { className: "grid gap-1.5" }, /* @__PURE__ */ React3.createElement("span", { className: "text-sm font-medium text-slate-700" }, "\u7F16\u8F91\u9875\u9762"), /* @__PURE__ */ React3.createElement(
405
+ "select",
406
+ {
407
+ value: activePageIndex,
408
+ onChange: (event) => setActivePageIndex(Number(event.target.value)),
409
+ className: "rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
410
+ },
411
+ pageOptions.map((index) => /* @__PURE__ */ React3.createElement("option", { key: index, value: index }, "\u7B2C ", index + 1, " \u9875"))
412
+ ))), canEditPage ? /* @__PURE__ */ React3.createElement("div", { className: "mt-4" }, /* @__PURE__ */ React3.createElement("div", { className: "mb-3 flex gap-2" }, /* @__PURE__ */ React3.createElement(
473
413
  "button",
474
414
  {
475
415
  type: "button",
@@ -478,7 +418,8 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
478
418
  pages: value.pages.map(
479
419
  (p, index) => index === activePageIndex ? { ...p, elements: [...p.elements, createTextElement(index)] } : p
480
420
  )
481
- })
421
+ }),
422
+ className: "rounded-lg bg-slate-900 px-3 py-2 text-sm font-medium text-white"
482
423
  },
483
424
  "+ \u6587\u5B57"
484
425
  ), /* @__PURE__ */ React3.createElement(
@@ -490,27 +431,38 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
490
431
  pages: value.pages.map(
491
432
  (p, index) => index === activePageIndex ? { ...p, elements: [...p.elements, createImageElement(index)] } : p
492
433
  )
493
- })
434
+ }),
435
+ className: "rounded-lg bg-sky-600 px-3 py-2 text-sm font-medium text-white"
494
436
  },
495
437
  "+ \u56FE\u7247"
496
- )), /* @__PURE__ */ React3.createElement("div", { style: { display: "grid", gap: 10, maxHeight: 340, overflow: "auto" } }, (page?.elements ?? []).map((element) => /* @__PURE__ */ React3.createElement("div", { key: element.id, style: { border: "1px solid #334155", borderRadius: 10, padding: 10 } }, /* @__PURE__ */ React3.createElement("div", { style: { marginBottom: 8 } }, element.type.toUpperCase()), element.type === "text" ? /* @__PURE__ */ React3.createElement(
438
+ )), /* @__PURE__ */ React3.createElement("div", { className: "grid max-h-[340px] gap-2.5 overflow-auto pr-1" }, (page?.elements ?? []).map((element) => /* @__PURE__ */ React3.createElement("div", { key: element.id, className: "rounded-xl border border-slate-200 bg-slate-50 p-3" }, /* @__PURE__ */ React3.createElement("div", { className: "mb-2 text-xs font-semibold tracking-wide text-slate-500" }, element.type.toUpperCase()), element.type === "text" ? /* @__PURE__ */ React3.createElement(
497
439
  "textarea",
498
440
  {
499
441
  value: element.content,
500
442
  onChange: (event) => updateElement(element.id, { content: event.target.value }),
501
443
  rows: 3,
502
- style: { width: "100%" }
444
+ className: "w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
503
445
  }
504
- ) : /* @__PURE__ */ React3.createElement(
446
+ ) : /* @__PURE__ */ React3.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React3.createElement(
505
447
  "input",
506
448
  {
507
449
  type: "url",
508
450
  value: element.src,
509
451
  onChange: (event) => updateElement(element.id, { src: event.target.value }),
510
- style: { width: "100%" }
452
+ className: "w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
453
+ }
454
+ ), /* @__PURE__ */ React3.createElement("label", { className: "inline-flex items-center gap-2 text-sm text-slate-700" }, /* @__PURE__ */ React3.createElement(
455
+ "input",
456
+ {
457
+ type: "checkbox",
458
+ checked: Boolean(element.isBackground),
459
+ onChange: (event) => updateElement(element.id, { isBackground: event.target.checked }),
460
+ className: "h-4 w-4 rounded border-slate-300 text-sky-600"
511
461
  }
512
- ))))) : null);
462
+ ), "\u4F5C\u4E3A\u672C\u9875\u80CC\u666F\u56FE")))))) : null);
513
463
  };
464
+
465
+ // src/festivalCard/components/FestivalCardStudio.tsx
514
466
  var FestivalCardStudio = ({ initialConfig, fetchConfig, onSave }) => {
515
467
  const { config, setConfig, loading, save, saving } = useFestivalCardConfig({
516
468
  initialConfig: normalizeFestivalCardConfig(initialConfig),
@@ -518,18 +470,135 @@ var FestivalCardStudio = ({ initialConfig, fetchConfig, onSave }) => {
518
470
  onSave
519
471
  });
520
472
  if (loading) return /* @__PURE__ */ React3.createElement("div", null, "\u52A0\u8F7D\u4E2D...");
521
- return /* @__PURE__ */ React3.createElement("div", { style: { display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 16 } }, /* @__PURE__ */ React3.createElement(FestivalCardBook3D, { config }), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(FestivalCardConfigEditor, { value: config, onChange: setConfig }), onSave ? /* @__PURE__ */ React3.createElement(
473
+ return /* @__PURE__ */ React3.createElement("div", { className: "grid gap-4 lg:grid-cols-[1.45fr_1fr]" }, /* @__PURE__ */ React3.createElement(FestivalCardBook3D, { config, className: "h-full" }), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(FestivalCardConfigEditor, { value: config, onChange: setConfig }), onSave ? /* @__PURE__ */ React3.createElement(
522
474
  "button",
523
475
  {
524
476
  type: "button",
525
477
  onClick: () => void save(),
526
478
  disabled: saving,
527
- style: { marginTop: 12, width: "100%", padding: "10px 16px" }
479
+ className: "mt-3 w-full rounded-lg bg-slate-900 px-4 py-2.5 text-sm font-medium text-white disabled:opacity-60"
528
480
  },
529
481
  saving ? "\u4FDD\u5B58\u4E2D..." : "\u4FDD\u5B58\u914D\u7F6E"
530
482
  ) : null));
531
483
  };
532
484
 
485
+ // src/festivalCard/components/FestivalCardConfigPage.tsx
486
+ var FestivalCardConfigPage = ({
487
+ apiBase = "/api/festivalCard",
488
+ cardId,
489
+ mainPagePath = "/festivalCard"
490
+ }) => {
491
+ const [list, setList] = useState([]);
492
+ const [selectedId, setSelectedId] = useState(cardId || "default-festival-card");
493
+ const parseListResponse2 = (data) => {
494
+ if (!data || typeof data !== "object") return [];
495
+ const payload = data.data;
496
+ if (!Array.isArray(payload)) return [];
497
+ return payload.filter((item) => Boolean(item && typeof item === "object" && typeof item.id === "string")).map((item) => ({ id: item.id, name: item.name }));
498
+ };
499
+ const parseConfigResponse2 = (data) => {
500
+ if (!data || typeof data !== "object") return normalizeFestivalCardConfig();
501
+ const payload = data.data;
502
+ if (!payload || typeof payload !== "object") return normalizeFestivalCardConfig();
503
+ return normalizeFestivalCardConfig(payload);
504
+ };
505
+ const reloadList = useCallback(async () => {
506
+ const response = await fetch(apiBase, { cache: "no-store" });
507
+ const data = await response.json();
508
+ setList(parseListResponse2(data));
509
+ }, [apiBase]);
510
+ useEffect(() => {
511
+ void reloadList();
512
+ }, [reloadList]);
513
+ const fetchConfig = async () => {
514
+ const response = await fetch(`${apiBase}/${encodeURIComponent(selectedId)}`, { cache: "no-store" });
515
+ const data = await response.json();
516
+ return parseConfigResponse2(data);
517
+ };
518
+ const saveConfig = async (config) => {
519
+ await fetch(`${apiBase}/${encodeURIComponent(selectedId)}`, {
520
+ method: "PUT",
521
+ headers: { "Content-Type": "application/json" },
522
+ body: JSON.stringify({ config })
523
+ });
524
+ await reloadList();
525
+ };
526
+ const createNew = async () => {
527
+ const name = window.prompt("\u8BF7\u8F93\u5165\u65B0\u5361\u7247\u540D\u79F0");
528
+ if (!name) return;
529
+ const id = `festival-${Date.now()}`;
530
+ const config = normalizeFestivalCardConfig({
531
+ id,
532
+ name
533
+ });
534
+ await fetch(`${apiBase}/${encodeURIComponent(id)}`, {
535
+ method: "PUT",
536
+ headers: { "Content-Type": "application/json" },
537
+ body: JSON.stringify({ config })
538
+ });
539
+ setSelectedId(id);
540
+ await reloadList();
541
+ };
542
+ const mainLink = useMemo(() => `${mainPagePath}?cardId=${encodeURIComponent(selectedId)}`, [mainPagePath, selectedId]);
543
+ return /* @__PURE__ */ React3.createElement("div", { className: "grid gap-3" }, /* @__PURE__ */ React3.createElement("div", { className: "flex flex-wrap items-center gap-2" }, /* @__PURE__ */ React3.createElement(
544
+ "select",
545
+ {
546
+ value: selectedId,
547
+ onChange: (event) => setSelectedId(event.target.value),
548
+ className: "min-w-[200px] rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
549
+ },
550
+ list.map((item) => /* @__PURE__ */ React3.createElement("option", { key: item.id, value: item.id }, item.name || item.id))
551
+ ), /* @__PURE__ */ React3.createElement("button", { type: "button", onClick: () => void createNew(), className: "rounded-lg bg-slate-900 px-3 py-2 text-sm font-medium text-white" }, "\u65B0\u5EFA\u5361\u7247"), /* @__PURE__ */ React3.createElement("a", { href: mainLink, className: "rounded-lg border border-sky-200 bg-sky-50 px-3 py-2 text-sm text-sky-700" }, "\u6253\u5F00\u4E3B\u9875\u9762")), /* @__PURE__ */ React3.createElement(FestivalCardStudio, { fetchConfig, onSave: saveConfig }));
552
+ };
553
+ var isSummary = (value) => {
554
+ if (!value || typeof value !== "object") return false;
555
+ return typeof value.id === "string";
556
+ };
557
+ var parseListResponse = (data) => {
558
+ if (!data || typeof data !== "object") return [];
559
+ const payload = data.data;
560
+ if (!Array.isArray(payload)) return [];
561
+ return payload.filter(isSummary).map((item) => ({ id: item.id, name: item.name }));
562
+ };
563
+ var parseConfigResponse = (data) => {
564
+ if (!data || typeof data !== "object") return null;
565
+ const payload = data.data;
566
+ if (!payload || typeof payload !== "object") return null;
567
+ if (!Array.isArray(payload.pages)) return null;
568
+ return payload;
569
+ };
570
+ var FestivalCardManagedPage = ({
571
+ apiBase = "/api/festivalCard",
572
+ cardId,
573
+ configPagePath = "/festivalCard/config"
574
+ }) => {
575
+ const [currentCardId, setCurrentCardId] = useState(cardId || "");
576
+ const [config, setConfig] = useState(null);
577
+ const [loading, setLoading] = useState(true);
578
+ useEffect(() => {
579
+ const fetchList = async () => {
580
+ const response = await fetch(apiBase, { cache: "no-store" });
581
+ const data = await response.json();
582
+ const items = parseListResponse(data);
583
+ const first = items[0];
584
+ if (!currentCardId && first) {
585
+ setCurrentCardId(first.id);
586
+ }
587
+ };
588
+ void fetchList();
589
+ }, [apiBase, currentCardId]);
590
+ useEffect(() => {
591
+ if (!currentCardId) return;
592
+ setLoading(true);
593
+ void fetch(`${apiBase}/${encodeURIComponent(currentCardId)}`, { cache: "no-store" }).then((res) => res.json()).then((data) => setConfig(parseConfigResponse(data))).finally(() => setLoading(false));
594
+ }, [apiBase, currentCardId]);
595
+ const configLink = useMemo(() => {
596
+ if (!currentCardId) return configPagePath;
597
+ return `${configPagePath}?cardId=${encodeURIComponent(currentCardId)}`;
598
+ }, [configPagePath, currentCardId]);
599
+ return /* @__PURE__ */ React3.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React3.createElement("div", { className: "flex justify-end" }, /* @__PURE__ */ React3.createElement("a", { href: configLink, className: "rounded-lg border border-slate-700 bg-slate-900 px-3 py-1.5 text-sm text-sky-300" }, "\u8FDB\u5165\u914D\u7F6E\u9875")), /* @__PURE__ */ React3.createElement("div", null, loading || !config ? /* @__PURE__ */ React3.createElement("div", { className: "py-12 text-center text-slate-400" }, "\u52A0\u8F7D\u4E2D...") : /* @__PURE__ */ React3.createElement(FestivalCardBook3D, { config })));
600
+ };
601
+
533
602
  // src/festivalCard/server/db.ts
534
603
  var dbAdapter = null;
535
604
  var getFestivalCardDb = () => dbAdapter;
@@ -539,6 +608,16 @@ var FestivalCardService = class {
539
608
  constructor(options) {
540
609
  this.db = options?.db || getFestivalCardDb();
541
610
  }
611
+ async listConfigs() {
612
+ if (!this.db) {
613
+ return [{ id: DEFAULT_FESTIVAL_CARD_CONFIG.id || "default-festival-card", name: DEFAULT_FESTIVAL_CARD_CONFIG.name }];
614
+ }
615
+ if (this.db.listConfigs) {
616
+ const list = await this.db.listConfigs();
617
+ return list.length > 0 ? list : [{ id: DEFAULT_FESTIVAL_CARD_CONFIG.id || "default-festival-card", name: DEFAULT_FESTIVAL_CARD_CONFIG.name }];
618
+ }
619
+ return [{ id: DEFAULT_FESTIVAL_CARD_CONFIG.id || "default-festival-card", name: DEFAULT_FESTIVAL_CARD_CONFIG.name }];
620
+ }
542
621
  async getConfig(cardId = "default-festival-card") {
543
622
  if (!this.db) return DEFAULT_FESTIVAL_CARD_CONFIG;
544
623
  const config = await this.db.getConfig(cardId);
@@ -550,18 +629,38 @@ var FestivalCardService = class {
550
629
  await this.db.saveConfig(cardId, normalized);
551
630
  return normalized;
552
631
  }
632
+ async deleteConfig(cardId) {
633
+ if (!this.db?.deleteConfig) return;
634
+ await this.db.deleteConfig(cardId);
635
+ }
553
636
  };
554
637
  var memoryStore = /* @__PURE__ */ new Map();
638
+ var defaultId = DEFAULT_FESTIVAL_CARD_CONFIG.id || "default-festival-card";
639
+ if (!memoryStore.has(defaultId)) {
640
+ memoryStore.set(defaultId, DEFAULT_FESTIVAL_CARD_CONFIG);
641
+ }
555
642
  var createInMemoryFestivalCardDb = () => ({
643
+ listConfigs() {
644
+ return Promise.resolve(
645
+ Array.from(memoryStore.entries()).map(([id, config]) => ({
646
+ id,
647
+ name: config.name || id
648
+ }))
649
+ );
650
+ },
556
651
  getConfig(id) {
557
652
  return Promise.resolve(memoryStore.get(id) || null);
558
653
  },
559
654
  saveConfig(id, config) {
560
655
  memoryStore.set(id, config);
561
656
  return Promise.resolve();
657
+ },
658
+ deleteConfig(id) {
659
+ memoryStore.delete(id);
660
+ return Promise.resolve();
562
661
  }
563
662
  });
564
663
 
565
- export { DEFAULT_FESTIVAL_CARD_CONFIG, FestivalCardBook3D, FestivalCardConfigEditor, FestivalCardPageRenderer, FestivalCardService, FestivalCardStudio, createInMemoryFestivalCardDb, normalizeFestivalCardConfig, resizeFestivalCardPages, useFestivalCardConfig };
664
+ export { DEFAULT_FESTIVAL_CARD_CONFIG, FestivalCardBook3D, FestivalCardConfigEditor, FestivalCardConfigPage, FestivalCardManagedPage, FestivalCardPageRenderer, FestivalCardService, FestivalCardStudio, createInMemoryFestivalCardDb, normalizeFestivalCardConfig, resizeFestivalCardPages, useFestivalCardConfig };
566
665
  //# sourceMappingURL=index.mjs.map
567
666
  //# sourceMappingURL=index.mjs.map