@legenki/print2medusa 0.8.2 → 0.9.2
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/.medusa/server/src/admin/index.js +158 -2
- package/.medusa/server/src/admin/index.mjs +159 -3
- package/.medusa/server/src/api/admin/printful/bundles/route.js +79 -0
- package/.medusa/server/src/api/admin/printful/design/route.js +38 -0
- package/.medusa/server/src/api/admin/printful/prompts/route.js +55 -0
- package/.medusa/server/src/utils/bundle.js +243 -0
- package/.medusa/server/src/utils/design.js +438 -0
- package/.medusa/server/src/utils/printful-client.js +23 -1
- package/.medusa/server/src/utils/prompt.js +423 -0
- package/.medusa/server/src/workflows/apply-order-status.js +16 -5
- package/.medusa/server/src/workflows/create-printful-order.js +20 -3
- package/.medusa/server/src/workflows/sync-products.js +68 -1
- package/README.md +91 -0
- package/package.json +1 -1
|
@@ -307,6 +307,16 @@ const PrintfulSyncWidget = () => {
|
|
|
307
307
|
adminSdk.defineWidgetConfig({
|
|
308
308
|
zone: "product.list.before"
|
|
309
309
|
});
|
|
310
|
+
const CLASS_LABEL = {
|
|
311
|
+
apparel: "Apparel",
|
|
312
|
+
embroidery: "Embroidered",
|
|
313
|
+
print_media: "Print media"
|
|
314
|
+
};
|
|
315
|
+
const CLASS_COLOUR = {
|
|
316
|
+
apparel: "blue",
|
|
317
|
+
embroidery: "orange",
|
|
318
|
+
print_media: "green"
|
|
319
|
+
};
|
|
310
320
|
const STATUS_COLOUR = {
|
|
311
321
|
success: "green",
|
|
312
322
|
running: "orange",
|
|
@@ -334,14 +344,22 @@ const PrintfulPage = () => {
|
|
|
334
344
|
const [syncs, setSyncs] = react.useState([]);
|
|
335
345
|
const [health, setHealth] = react.useState(null);
|
|
336
346
|
const [stats, setStats] = react.useState(null);
|
|
347
|
+
const [designs, setDesigns] = react.useState([]);
|
|
348
|
+
const [bundles, setBundles] = react.useState([]);
|
|
349
|
+
const [promptProducts, setPromptProducts] = react.useState([]);
|
|
350
|
+
const [styles, setStyles] = react.useState([]);
|
|
351
|
+
const [style, setStyle] = react.useState("editorial");
|
|
352
|
+
const [artwork, setArtwork] = react.useState("");
|
|
337
353
|
const [loading, setLoading] = react.useState(true);
|
|
338
354
|
const [clearing, setClearing] = react.useState(false);
|
|
339
355
|
const load = react.useCallback(async () => {
|
|
340
356
|
var _a2;
|
|
341
|
-
const [h, w, s] = await Promise.allSettled([
|
|
357
|
+
const [h, w, s, d, b] = await Promise.allSettled([
|
|
342
358
|
fetch("/admin/printful/history", { credentials: "include" }),
|
|
343
359
|
fetch("/admin/printful/health", { credentials: "include" }),
|
|
344
|
-
fetch("/admin/printful/statistics", { credentials: "include" })
|
|
360
|
+
fetch("/admin/printful/statistics", { credentials: "include" }),
|
|
361
|
+
fetch("/admin/printful/design", { credentials: "include" }),
|
|
362
|
+
fetch("/admin/printful/bundles", { credentials: "include" })
|
|
345
363
|
]);
|
|
346
364
|
if (h.status === "fulfilled" && h.value.ok) {
|
|
347
365
|
const body = await h.value.json();
|
|
@@ -354,11 +372,40 @@ const PrintfulPage = () => {
|
|
|
354
372
|
const body = await s.value.json();
|
|
355
373
|
setStats(((_a2 = body.statistics) == null ? void 0 : _a2[0]) ?? null);
|
|
356
374
|
}
|
|
375
|
+
if (d.status === "fulfilled" && d.value.ok) {
|
|
376
|
+
const body = await d.value.json();
|
|
377
|
+
setDesigns(body.designs ?? []);
|
|
378
|
+
}
|
|
379
|
+
if (b.status === "fulfilled" && b.value.ok) {
|
|
380
|
+
const body = await b.value.json();
|
|
381
|
+
setBundles(body.bundles ?? []);
|
|
382
|
+
}
|
|
357
383
|
setLoading(false);
|
|
358
384
|
}, []);
|
|
385
|
+
const loadPrompts = react.useCallback(async () => {
|
|
386
|
+
const params = new URLSearchParams({ style });
|
|
387
|
+
if (artwork.trim()) {
|
|
388
|
+
params.set("artwork", artwork.trim());
|
|
389
|
+
}
|
|
390
|
+
try {
|
|
391
|
+
const res = await fetch(`/admin/printful/prompts?${params}`, {
|
|
392
|
+
credentials: "include"
|
|
393
|
+
});
|
|
394
|
+
if (!res.ok) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const body = await res.json();
|
|
398
|
+
setPromptProducts(body.products ?? []);
|
|
399
|
+
setStyles(body.styles ?? []);
|
|
400
|
+
} catch {
|
|
401
|
+
}
|
|
402
|
+
}, [style, artwork]);
|
|
359
403
|
react.useEffect(() => {
|
|
360
404
|
void load();
|
|
361
405
|
}, [load]);
|
|
406
|
+
react.useEffect(() => {
|
|
407
|
+
void loadPrompts();
|
|
408
|
+
}, [loadPrompts]);
|
|
362
409
|
const latest = syncs[0];
|
|
363
410
|
const running = (latest == null ? void 0 : latest.status) === "running";
|
|
364
411
|
react.useEffect(() => {
|
|
@@ -472,6 +519,115 @@ const PrintfulPage = () => {
|
|
|
472
519
|
] })
|
|
473
520
|
] })
|
|
474
521
|
] }),
|
|
522
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 flex flex-col gap-3", children: [
|
|
523
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: "Design parameters" }),
|
|
524
|
+
designs.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: loading ? "Loading…" : "No products carry design parameters yet. Run a sync." }) : designs.map((d) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
525
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
526
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: d.title }),
|
|
527
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: CLASS_COLOUR[d.product_class] ?? "grey", children: CLASS_LABEL[d.product_class] ?? d.product_class })
|
|
528
|
+
] }),
|
|
529
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
|
|
530
|
+
d.technique ?? "—",
|
|
531
|
+
d.brand ? ` · ${d.brand}${d.model ? ` ${d.model}` : ""}` : "",
|
|
532
|
+
d.material ? ` · ${d.material}` : ""
|
|
533
|
+
] }),
|
|
534
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
|
|
535
|
+
"Design goes on: ",
|
|
536
|
+
d.core_placements.join(", ") || "—",
|
|
537
|
+
d.placements.length > d.core_placements.length ? ` (${d.placements.length - d.core_placements.length} more available)` : ""
|
|
538
|
+
] }),
|
|
539
|
+
d.colors.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 flex-wrap", children: [
|
|
540
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Base colours:" }),
|
|
541
|
+
d.colors.slice(0, 12).map((c) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
542
|
+
"span",
|
|
543
|
+
{
|
|
544
|
+
title: `${c.name}${c.hex ? ` ${c.hex}` : ""}`,
|
|
545
|
+
className: "inline-block h-4 w-4 rounded border border-ui-border-base",
|
|
546
|
+
style: c.hex ? { backgroundColor: c.hex } : void 0
|
|
547
|
+
},
|
|
548
|
+
c.name
|
|
549
|
+
)),
|
|
550
|
+
d.colors.length > 12 && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
|
|
551
|
+
"+",
|
|
552
|
+
d.colors.length - 12
|
|
553
|
+
] })
|
|
554
|
+
] }),
|
|
555
|
+
d.sizes.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
|
|
556
|
+
d.product_class === "print_media" ? "Sizes" : "Sizes",
|
|
557
|
+
":",
|
|
558
|
+
" ",
|
|
559
|
+
d.sizes.slice(0, 8).join(", "),
|
|
560
|
+
d.sizes.length > 8 ? ` +${d.sizes.length - 8}` : ""
|
|
561
|
+
] })
|
|
562
|
+
] }, d.product_id))
|
|
563
|
+
] }),
|
|
564
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 flex flex-col gap-3", children: [
|
|
565
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: "Bundles" }),
|
|
566
|
+
bundles.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: loading ? "Loading…" : "No bundles yet. Add printful_bundle_members to a variant's metadata to make it a bundle." }) : bundles.map((b) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
567
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
568
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: b.title }),
|
|
569
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: b.available ? "green" : "red", children: b.available ? "all members in stock" : "member unavailable" }),
|
|
570
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: b.status === "published" ? "green" : "grey", children: b.status }),
|
|
571
|
+
b.missing_count > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { color: "orange", children: [
|
|
572
|
+
b.missing_count,
|
|
573
|
+
" member",
|
|
574
|
+
b.missing_count === 1 ? "" : "s",
|
|
575
|
+
" not found"
|
|
576
|
+
] })
|
|
577
|
+
] }),
|
|
578
|
+
b.members.map((m) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
579
|
+
ui.Text,
|
|
580
|
+
{
|
|
581
|
+
size: "small",
|
|
582
|
+
className: "text-ui-fg-subtle pl-3",
|
|
583
|
+
children: [
|
|
584
|
+
m.quantity,
|
|
585
|
+
"× ",
|
|
586
|
+
m.product_title ?? "—",
|
|
587
|
+
m.title ? ` · ${m.title}` : "",
|
|
588
|
+
m.status === "missing" ? " — variant not found" : m.status === "active" || m.status === "unknown" ? "" : ` — ${m.status.replace(/_/g, " ")}`
|
|
589
|
+
]
|
|
590
|
+
},
|
|
591
|
+
m.variant_id
|
|
592
|
+
))
|
|
593
|
+
] }, b.variant_id))
|
|
594
|
+
] }),
|
|
595
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 flex flex-col gap-3", children: [
|
|
596
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: "Mockup prompts" }),
|
|
597
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Paste these into an image model. Printful’s own generator renders a product on a plain background — right for a catalogue thumbnail, not for an editorial mockup." }),
|
|
598
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
|
|
599
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: style, onValueChange: setStyle, children: [
|
|
600
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { className: "w-40", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Style" }) }),
|
|
601
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: styles.map((s) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: s.id, children: s.label }, s.id)) })
|
|
602
|
+
] }),
|
|
603
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
604
|
+
ui.Input,
|
|
605
|
+
{
|
|
606
|
+
className: "flex-1 min-w-64",
|
|
607
|
+
placeholder: "What is the artwork? (optional)",
|
|
608
|
+
value: artwork,
|
|
609
|
+
onChange: (e) => setArtwork(e.target.value)
|
|
610
|
+
}
|
|
611
|
+
)
|
|
612
|
+
] }),
|
|
613
|
+
promptProducts.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: loading ? "Loading…" : "No prompts yet. Run a sync so products carry design parameters." }) : promptProducts.map((p) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
614
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: p.title }),
|
|
615
|
+
p.prompts.map((prompt, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
616
|
+
"div",
|
|
617
|
+
{
|
|
618
|
+
className: "flex items-start gap-2 rounded-md border border-ui-border-base p-2",
|
|
619
|
+
children: [
|
|
620
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 flex-1", children: [
|
|
621
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-muted", children: prompt.label }),
|
|
622
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: prompt.text })
|
|
623
|
+
] }),
|
|
624
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Copy, { content: prompt.text })
|
|
625
|
+
]
|
|
626
|
+
},
|
|
627
|
+
`${p.product_id}-${i}`
|
|
628
|
+
))
|
|
629
|
+
] }, p.product_id))
|
|
630
|
+
] }),
|
|
475
631
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 flex flex-col gap-2", children: [
|
|
476
632
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: "Recent syncs" }),
|
|
477
633
|
syncs.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: loading ? "Loading…" : "No syncs yet." }) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
|
|
3
|
-
import { Container, Heading, Badge, Text, Button, toast, Prompt, Table } from "@medusajs/ui";
|
|
3
|
+
import { Container, Heading, Badge, Text, Button, toast, Prompt, Select, Input, Copy, Table } from "@medusajs/ui";
|
|
4
4
|
import { defaultCurrencies } from "@medusajs/framework/utils";
|
|
5
5
|
import { useState, useEffect, useCallback } from "react";
|
|
6
6
|
function minorUnitFactor(currencyCode) {
|
|
@@ -306,6 +306,16 @@ const PrintfulSyncWidget = () => {
|
|
|
306
306
|
defineWidgetConfig({
|
|
307
307
|
zone: "product.list.before"
|
|
308
308
|
});
|
|
309
|
+
const CLASS_LABEL = {
|
|
310
|
+
apparel: "Apparel",
|
|
311
|
+
embroidery: "Embroidered",
|
|
312
|
+
print_media: "Print media"
|
|
313
|
+
};
|
|
314
|
+
const CLASS_COLOUR = {
|
|
315
|
+
apparel: "blue",
|
|
316
|
+
embroidery: "orange",
|
|
317
|
+
print_media: "green"
|
|
318
|
+
};
|
|
309
319
|
const STATUS_COLOUR = {
|
|
310
320
|
success: "green",
|
|
311
321
|
running: "orange",
|
|
@@ -333,14 +343,22 @@ const PrintfulPage = () => {
|
|
|
333
343
|
const [syncs, setSyncs] = useState([]);
|
|
334
344
|
const [health, setHealth] = useState(null);
|
|
335
345
|
const [stats, setStats] = useState(null);
|
|
346
|
+
const [designs, setDesigns] = useState([]);
|
|
347
|
+
const [bundles, setBundles] = useState([]);
|
|
348
|
+
const [promptProducts, setPromptProducts] = useState([]);
|
|
349
|
+
const [styles, setStyles] = useState([]);
|
|
350
|
+
const [style, setStyle] = useState("editorial");
|
|
351
|
+
const [artwork, setArtwork] = useState("");
|
|
336
352
|
const [loading, setLoading] = useState(true);
|
|
337
353
|
const [clearing, setClearing] = useState(false);
|
|
338
354
|
const load = useCallback(async () => {
|
|
339
355
|
var _a2;
|
|
340
|
-
const [h, w, s] = await Promise.allSettled([
|
|
356
|
+
const [h, w, s, d, b] = await Promise.allSettled([
|
|
341
357
|
fetch("/admin/printful/history", { credentials: "include" }),
|
|
342
358
|
fetch("/admin/printful/health", { credentials: "include" }),
|
|
343
|
-
fetch("/admin/printful/statistics", { credentials: "include" })
|
|
359
|
+
fetch("/admin/printful/statistics", { credentials: "include" }),
|
|
360
|
+
fetch("/admin/printful/design", { credentials: "include" }),
|
|
361
|
+
fetch("/admin/printful/bundles", { credentials: "include" })
|
|
344
362
|
]);
|
|
345
363
|
if (h.status === "fulfilled" && h.value.ok) {
|
|
346
364
|
const body = await h.value.json();
|
|
@@ -353,11 +371,40 @@ const PrintfulPage = () => {
|
|
|
353
371
|
const body = await s.value.json();
|
|
354
372
|
setStats(((_a2 = body.statistics) == null ? void 0 : _a2[0]) ?? null);
|
|
355
373
|
}
|
|
374
|
+
if (d.status === "fulfilled" && d.value.ok) {
|
|
375
|
+
const body = await d.value.json();
|
|
376
|
+
setDesigns(body.designs ?? []);
|
|
377
|
+
}
|
|
378
|
+
if (b.status === "fulfilled" && b.value.ok) {
|
|
379
|
+
const body = await b.value.json();
|
|
380
|
+
setBundles(body.bundles ?? []);
|
|
381
|
+
}
|
|
356
382
|
setLoading(false);
|
|
357
383
|
}, []);
|
|
384
|
+
const loadPrompts = useCallback(async () => {
|
|
385
|
+
const params = new URLSearchParams({ style });
|
|
386
|
+
if (artwork.trim()) {
|
|
387
|
+
params.set("artwork", artwork.trim());
|
|
388
|
+
}
|
|
389
|
+
try {
|
|
390
|
+
const res = await fetch(`/admin/printful/prompts?${params}`, {
|
|
391
|
+
credentials: "include"
|
|
392
|
+
});
|
|
393
|
+
if (!res.ok) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const body = await res.json();
|
|
397
|
+
setPromptProducts(body.products ?? []);
|
|
398
|
+
setStyles(body.styles ?? []);
|
|
399
|
+
} catch {
|
|
400
|
+
}
|
|
401
|
+
}, [style, artwork]);
|
|
358
402
|
useEffect(() => {
|
|
359
403
|
void load();
|
|
360
404
|
}, [load]);
|
|
405
|
+
useEffect(() => {
|
|
406
|
+
void loadPrompts();
|
|
407
|
+
}, [loadPrompts]);
|
|
361
408
|
const latest = syncs[0];
|
|
362
409
|
const running = (latest == null ? void 0 : latest.status) === "running";
|
|
363
410
|
useEffect(() => {
|
|
@@ -471,6 +518,115 @@ const PrintfulPage = () => {
|
|
|
471
518
|
] })
|
|
472
519
|
] })
|
|
473
520
|
] }),
|
|
521
|
+
/* @__PURE__ */ jsxs("div", { className: "px-6 py-4 flex flex-col gap-3", children: [
|
|
522
|
+
/* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: "Design parameters" }),
|
|
523
|
+
designs.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: loading ? "Loading…" : "No products carry design parameters yet. Run a sync." }) : designs.map((d) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
524
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
525
|
+
/* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: d.title }),
|
|
526
|
+
/* @__PURE__ */ jsx(Badge, { color: CLASS_COLOUR[d.product_class] ?? "grey", children: CLASS_LABEL[d.product_class] ?? d.product_class })
|
|
527
|
+
] }),
|
|
528
|
+
/* @__PURE__ */ jsxs(Text, { size: "small", className: "text-ui-fg-subtle", children: [
|
|
529
|
+
d.technique ?? "—",
|
|
530
|
+
d.brand ? ` · ${d.brand}${d.model ? ` ${d.model}` : ""}` : "",
|
|
531
|
+
d.material ? ` · ${d.material}` : ""
|
|
532
|
+
] }),
|
|
533
|
+
/* @__PURE__ */ jsxs(Text, { size: "small", className: "text-ui-fg-subtle", children: [
|
|
534
|
+
"Design goes on: ",
|
|
535
|
+
d.core_placements.join(", ") || "—",
|
|
536
|
+
d.placements.length > d.core_placements.length ? ` (${d.placements.length - d.core_placements.length} more available)` : ""
|
|
537
|
+
] }),
|
|
538
|
+
d.colors.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 flex-wrap", children: [
|
|
539
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Base colours:" }),
|
|
540
|
+
d.colors.slice(0, 12).map((c) => /* @__PURE__ */ jsx(
|
|
541
|
+
"span",
|
|
542
|
+
{
|
|
543
|
+
title: `${c.name}${c.hex ? ` ${c.hex}` : ""}`,
|
|
544
|
+
className: "inline-block h-4 w-4 rounded border border-ui-border-base",
|
|
545
|
+
style: c.hex ? { backgroundColor: c.hex } : void 0
|
|
546
|
+
},
|
|
547
|
+
c.name
|
|
548
|
+
)),
|
|
549
|
+
d.colors.length > 12 && /* @__PURE__ */ jsxs(Text, { size: "small", className: "text-ui-fg-subtle", children: [
|
|
550
|
+
"+",
|
|
551
|
+
d.colors.length - 12
|
|
552
|
+
] })
|
|
553
|
+
] }),
|
|
554
|
+
d.sizes.length > 0 && /* @__PURE__ */ jsxs(Text, { size: "small", className: "text-ui-fg-subtle", children: [
|
|
555
|
+
d.product_class === "print_media" ? "Sizes" : "Sizes",
|
|
556
|
+
":",
|
|
557
|
+
" ",
|
|
558
|
+
d.sizes.slice(0, 8).join(", "),
|
|
559
|
+
d.sizes.length > 8 ? ` +${d.sizes.length - 8}` : ""
|
|
560
|
+
] })
|
|
561
|
+
] }, d.product_id))
|
|
562
|
+
] }),
|
|
563
|
+
/* @__PURE__ */ jsxs("div", { className: "px-6 py-4 flex flex-col gap-3", children: [
|
|
564
|
+
/* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: "Bundles" }),
|
|
565
|
+
bundles.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: loading ? "Loading…" : "No bundles yet. Add printful_bundle_members to a variant's metadata to make it a bundle." }) : bundles.map((b) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
566
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
567
|
+
/* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: b.title }),
|
|
568
|
+
/* @__PURE__ */ jsx(Badge, { color: b.available ? "green" : "red", children: b.available ? "all members in stock" : "member unavailable" }),
|
|
569
|
+
/* @__PURE__ */ jsx(Badge, { color: b.status === "published" ? "green" : "grey", children: b.status }),
|
|
570
|
+
b.missing_count > 0 && /* @__PURE__ */ jsxs(Badge, { color: "orange", children: [
|
|
571
|
+
b.missing_count,
|
|
572
|
+
" member",
|
|
573
|
+
b.missing_count === 1 ? "" : "s",
|
|
574
|
+
" not found"
|
|
575
|
+
] })
|
|
576
|
+
] }),
|
|
577
|
+
b.members.map((m) => /* @__PURE__ */ jsxs(
|
|
578
|
+
Text,
|
|
579
|
+
{
|
|
580
|
+
size: "small",
|
|
581
|
+
className: "text-ui-fg-subtle pl-3",
|
|
582
|
+
children: [
|
|
583
|
+
m.quantity,
|
|
584
|
+
"× ",
|
|
585
|
+
m.product_title ?? "—",
|
|
586
|
+
m.title ? ` · ${m.title}` : "",
|
|
587
|
+
m.status === "missing" ? " — variant not found" : m.status === "active" || m.status === "unknown" ? "" : ` — ${m.status.replace(/_/g, " ")}`
|
|
588
|
+
]
|
|
589
|
+
},
|
|
590
|
+
m.variant_id
|
|
591
|
+
))
|
|
592
|
+
] }, b.variant_id))
|
|
593
|
+
] }),
|
|
594
|
+
/* @__PURE__ */ jsxs("div", { className: "px-6 py-4 flex flex-col gap-3", children: [
|
|
595
|
+
/* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: "Mockup prompts" }),
|
|
596
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Paste these into an image model. Printful’s own generator renders a product on a plain background — right for a catalogue thumbnail, not for an editorial mockup." }),
|
|
597
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
|
|
598
|
+
/* @__PURE__ */ jsxs(Select, { value: style, onValueChange: setStyle, children: [
|
|
599
|
+
/* @__PURE__ */ jsx(Select.Trigger, { className: "w-40", children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "Style" }) }),
|
|
600
|
+
/* @__PURE__ */ jsx(Select.Content, { children: styles.map((s) => /* @__PURE__ */ jsx(Select.Item, { value: s.id, children: s.label }, s.id)) })
|
|
601
|
+
] }),
|
|
602
|
+
/* @__PURE__ */ jsx(
|
|
603
|
+
Input,
|
|
604
|
+
{
|
|
605
|
+
className: "flex-1 min-w-64",
|
|
606
|
+
placeholder: "What is the artwork? (optional)",
|
|
607
|
+
value: artwork,
|
|
608
|
+
onChange: (e) => setArtwork(e.target.value)
|
|
609
|
+
}
|
|
610
|
+
)
|
|
611
|
+
] }),
|
|
612
|
+
promptProducts.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: loading ? "Loading…" : "No prompts yet. Run a sync so products carry design parameters." }) : promptProducts.map((p) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
613
|
+
/* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: p.title }),
|
|
614
|
+
p.prompts.map((prompt, i) => /* @__PURE__ */ jsxs(
|
|
615
|
+
"div",
|
|
616
|
+
{
|
|
617
|
+
className: "flex items-start gap-2 rounded-md border border-ui-border-base p-2",
|
|
618
|
+
children: [
|
|
619
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 flex-1", children: [
|
|
620
|
+
/* @__PURE__ */ jsx(Text, { size: "xsmall", className: "text-ui-fg-muted", children: prompt.label }),
|
|
621
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: prompt.text })
|
|
622
|
+
] }),
|
|
623
|
+
/* @__PURE__ */ jsx(Copy, { content: prompt.text })
|
|
624
|
+
]
|
|
625
|
+
},
|
|
626
|
+
`${p.product_id}-${i}`
|
|
627
|
+
))
|
|
628
|
+
] }, p.product_id))
|
|
629
|
+
] }),
|
|
474
630
|
/* @__PURE__ */ jsxs("div", { className: "px-6 py-4 flex flex-col gap-2", children: [
|
|
475
631
|
/* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: "Recent syncs" }),
|
|
476
632
|
syncs.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: loading ? "Loading…" : "No syncs yet." }) : /* @__PURE__ */ jsxs(Table, { children: [
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GET = GET;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const bundle_1 = require("../../../../utils/bundle");
|
|
6
|
+
/**
|
|
7
|
+
* Bundles and whether each can currently be sold.
|
|
8
|
+
*
|
|
9
|
+
* A pure read of the database — no Printful call. Availability comes from the
|
|
10
|
+
* `printful_availability_status` the sync already stamped on every member
|
|
11
|
+
* variant, which is the same source the sync's own bundle pass decides on. A
|
|
12
|
+
* live lookup here could disagree with the status the catalogue was last
|
|
13
|
+
* published against, and show the owner a bundle as sellable that the next
|
|
14
|
+
* sync is about to draft.
|
|
15
|
+
*
|
|
16
|
+
* Every member is listed, available or not, because the question the owner has
|
|
17
|
+
* when a bundle goes off sale is *which* member ran out.
|
|
18
|
+
*/
|
|
19
|
+
async function GET(req, res) {
|
|
20
|
+
const productModule = req.scope.resolve(utils_1.Modules.PRODUCT);
|
|
21
|
+
const products = await productModule.listProducts({}, { relations: ["variants"], take: 1000 });
|
|
22
|
+
// Members are variant ids on other products, so the whole catalogue has to
|
|
23
|
+
// be indexed before any bundle can be resolved.
|
|
24
|
+
const variantIndex = new Map();
|
|
25
|
+
for (const product of products) {
|
|
26
|
+
for (const variant of product.variants ?? []) {
|
|
27
|
+
const metadata = (variant.metadata ?? {});
|
|
28
|
+
variantIndex.set(variant.id, {
|
|
29
|
+
title: variant.title ?? variant.id,
|
|
30
|
+
product_title: product.title,
|
|
31
|
+
status: typeof metadata.printful_availability_status === "string"
|
|
32
|
+
? metadata.printful_availability_status
|
|
33
|
+
: "unknown",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const bundles = [];
|
|
38
|
+
for (const product of products) {
|
|
39
|
+
for (const variant of product.variants ?? []) {
|
|
40
|
+
const members = (0, bundle_1.bundleMembersOf)({
|
|
41
|
+
id: variant.id,
|
|
42
|
+
quantity: 1,
|
|
43
|
+
metadata: (variant.metadata ?? {}),
|
|
44
|
+
});
|
|
45
|
+
if (!members.length) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const resolved = members.map((member) => {
|
|
49
|
+
const found = variantIndex.get(member.variant_id);
|
|
50
|
+
return {
|
|
51
|
+
variant_id: member.variant_id,
|
|
52
|
+
quantity: member.quantity ?? 1,
|
|
53
|
+
// A member whose variant no longer exists is reported as such rather
|
|
54
|
+
// than hidden: it is the likeliest reason a bundle fails at Printful,
|
|
55
|
+
// and it is invisible everywhere else in the admin.
|
|
56
|
+
title: found?.title ?? null,
|
|
57
|
+
product_title: found?.product_title ?? null,
|
|
58
|
+
status: found?.status ?? "missing",
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
const availability = (0, bundle_1.planBundleAvailability)(resolved
|
|
62
|
+
.filter((m) => m.status !== "missing")
|
|
63
|
+
.map((m) => ({ printful_availability_status: m.status })));
|
|
64
|
+
bundles.push({
|
|
65
|
+
product_id: product.id,
|
|
66
|
+
variant_id: variant.id,
|
|
67
|
+
title: product.title,
|
|
68
|
+
variant_title: variant.title,
|
|
69
|
+
status: product.status,
|
|
70
|
+
available: availability.available,
|
|
71
|
+
member_count: resolved.length,
|
|
72
|
+
missing_count: resolved.filter((m) => m.status === "missing").length,
|
|
73
|
+
members: resolved,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
res.status(200).json({ bundles });
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3ByaW50ZnVsL2J1bmRsZXMvcm91dGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFvQkEsa0JBMkVDO0FBOUZELHFEQUFtRDtBQUNuRCxxREFHaUM7QUFFakM7Ozs7Ozs7Ozs7OztHQVlHO0FBQ0ksS0FBSyxVQUFVLEdBQUcsQ0FBQyxHQUFrQixFQUFFLEdBQW1CO0lBQy9ELE1BQU0sYUFBYSxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGVBQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQTtJQUV4RCxNQUFNLFFBQVEsR0FBRyxNQUFNLGFBQWEsQ0FBQyxZQUFZLENBQy9DLEVBQUUsRUFDRixFQUFFLFNBQVMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsQ0FDeEMsQ0FBQTtJQUVELDJFQUEyRTtJQUMzRSxnREFBZ0Q7SUFDaEQsTUFBTSxZQUFZLEdBQUcsSUFBSSxHQUFHLEVBR3pCLENBQUE7SUFDSCxLQUFLLE1BQU0sT0FBTyxJQUFJLFFBQVEsRUFBRSxDQUFDO1FBQy9CLEtBQUssTUFBTSxPQUFPLElBQUksT0FBTyxDQUFDLFFBQVEsSUFBSSxFQUFFLEVBQUUsQ0FBQztZQUM3QyxNQUFNLFFBQVEsR0FBRyxDQUFDLE9BQU8sQ0FBQyxRQUFRLElBQUksRUFBRSxDQUE0QixDQUFBO1lBQ3BFLFlBQVksQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLEVBQUUsRUFBRTtnQkFDM0IsS0FBSyxFQUFFLE9BQU8sQ0FBQyxLQUFLLElBQUksT0FBTyxDQUFDLEVBQUU7Z0JBQ2xDLGFBQWEsRUFBRSxPQUFPLENBQUMsS0FBSztnQkFDNUIsTUFBTSxFQUNKLE9BQU8sUUFBUSxDQUFDLDRCQUE0QixLQUFLLFFBQVE7b0JBQ3ZELENBQUMsQ0FBQyxRQUFRLENBQUMsNEJBQTRCO29CQUN2QyxD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
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GET = GET;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const design_1 = require("../../../../utils/design");
|
|
6
|
+
/**
|
|
7
|
+
* Design parameters per product, read from the variant metadata the sync
|
|
8
|
+
* stamped.
|
|
9
|
+
*
|
|
10
|
+
* A pure read of the database — no Printful call. The sync already paid for
|
|
11
|
+
* the catalog lookups, and this page is opened far more often than the
|
|
12
|
+
* catalogue changes.
|
|
13
|
+
*
|
|
14
|
+
* Products whose variants carry no design metadata are omitted rather than
|
|
15
|
+
* listed empty: one was never enriched, the other has nothing to show, and
|
|
16
|
+
* rendering them the same way tells the owner something false.
|
|
17
|
+
*/
|
|
18
|
+
async function GET(req, res) {
|
|
19
|
+
const productModule = req.scope.resolve(utils_1.Modules.PRODUCT);
|
|
20
|
+
const products = await productModule.listProducts({}, { relations: ["variants"], take: 100 });
|
|
21
|
+
const designs = products
|
|
22
|
+
.map((product) => {
|
|
23
|
+
const summary = (0, design_1.summarizeProductDesign)(product.variants ?? []);
|
|
24
|
+
if (!summary) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
product_id: product.id,
|
|
29
|
+
title: product.title,
|
|
30
|
+
status: product.status,
|
|
31
|
+
variant_count: (product.variants ?? []).length,
|
|
32
|
+
...summary,
|
|
33
|
+
};
|
|
34
|
+
})
|
|
35
|
+
.filter((d) => d !== null);
|
|
36
|
+
res.status(200).json({ designs });
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3ByaW50ZnVsL2Rlc2lnbi9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQWdCQSxrQkF5QkM7QUF4Q0QscURBQW1EO0FBQ25ELHFEQUFpRTtBQUVqRTs7Ozs7Ozs7Ozs7R0FXRztBQUNJLEtBQUssVUFBVSxHQUFHLENBQUMsR0FBa0IsRUFBRSxHQUFtQjtJQUMvRCxNQUFNLGFBQWEsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxlQUFPLENBQUMsT0FBTyxDQUFDLENBQUE7SUFFeEQsTUFBTSxRQUFRLEdBQUcsTUFBTSxhQUFhLENBQUMsWUFBWSxDQUMvQyxFQUFFLEVBQ0YsRUFBRSxTQUFTLEVBQUUsQ0FBQyxVQUFVLENBQUMsRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLENBQ3ZDLENBQUE7SUFFRCxNQUFNLE9BQU8sR0FBRyxRQUFRO1NBQ3JCLEdBQUcsQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1FBQ2YsTUFBTSxPQUFPLEdBQUcsSUFBQSwrQkFBc0IsRUFBQyxPQUFPLENBQUMsUUFBUSxJQUFJLEVBQUUsQ0FBQyxDQUFBO1FBQzlELElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNiLE9BQU8sSUFBSSxDQUFBO1FBQ2IsQ0FBQztRQUNELE9BQU87WUFDTCxVQUFVLEVBQUUsT0FBTyxDQUFDLEVBQUU7WUFDdEIsS0FBSyxFQUFFLE9BQU8sQ0FBQyxLQUFLO1lBQ3BCLE1BQU0sRUFBRSxPQUFPLENBQUMsTUFBTTtZQUN0QixhQUFhLEVBQUUsQ0FBQyxPQUFPLENBQUMsUUFBUSxJQUFJLEVBQUUsQ0FBQyxDQUFDLE1BQU07WUFDOUMsR0FBRyxPQUFPO1NBQ1gsQ0FBQTtJQUNILENBQUMsQ0FBQztTQUNELE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBOEIsRUFBRSxDQUFDLENBQUMsS0FBSyxJQUFJLENBQUMsQ0FBQTtJQUV4RCxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUE7QUFDbkMsQ0FBQyJ9
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GET = GET;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const design_1 = require("../../../../utils/design");
|
|
6
|
+
const prompt_1 = require("../../../../utils/prompt");
|
|
7
|
+
/**
|
|
8
|
+
* Mockup prompts per product.
|
|
9
|
+
*
|
|
10
|
+
* A pure read of the database plus string assembly — no Printful call, and no
|
|
11
|
+
* image generation. The plugin writes prompts; generation happens wherever you
|
|
12
|
+
* paste them, which is what keeps API keys, rate-limit queues and file storage
|
|
13
|
+
* out of this repo.
|
|
14
|
+
*/
|
|
15
|
+
async function GET(req, res) {
|
|
16
|
+
const productModule = req.scope.resolve(utils_1.Modules.PRODUCT);
|
|
17
|
+
const query = req.query;
|
|
18
|
+
// An unrecognized style falls back to the first preset rather than
|
|
19
|
+
// erroring: a mistyped query parameter should still produce prompts.
|
|
20
|
+
const requested = typeof query.style === "string" ? query.style : undefined;
|
|
21
|
+
const style = prompt_1.PROMPT_STYLES.some((s) => s.id === requested)
|
|
22
|
+
? requested
|
|
23
|
+
: undefined;
|
|
24
|
+
const artwork = typeof query.artwork === "string" && query.artwork.trim()
|
|
25
|
+
? query.artwork.trim()
|
|
26
|
+
: undefined;
|
|
27
|
+
const products = await productModule.listProducts({}, { relations: ["variants"], take: 100 });
|
|
28
|
+
const items = products
|
|
29
|
+
.map((product) => {
|
|
30
|
+
const summary = (0, design_1.summarizeProductDesign)(product.variants ?? []);
|
|
31
|
+
if (!summary) {
|
|
32
|
+
// Never enriched by a sync. Omitted rather than listed with no
|
|
33
|
+
// prompts, which would read as "this product cannot be prompted for".
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const prompts = (0, prompt_1.buildProductPrompts)({ design: summary, style, artwork });
|
|
37
|
+
if (prompts.length === 0) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
product_id: product.id,
|
|
42
|
+
title: product.title,
|
|
43
|
+
product_class: summary.product_class,
|
|
44
|
+
prompts,
|
|
45
|
+
};
|
|
46
|
+
})
|
|
47
|
+
.filter((p) => p !== null);
|
|
48
|
+
res.status(200).json({
|
|
49
|
+
products: items,
|
|
50
|
+
styles: prompt_1.PROMPT_STYLES.map((s) => ({ id: s.id, label: s.label })),
|
|
51
|
+
style: style ?? prompt_1.PROMPT_STYLES[0].id,
|
|
52
|
+
artwork,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3ByaW50ZnVsL3Byb21wdHMvcm91dGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFpQkEsa0JBa0RDO0FBbEVELHFEQUFtRDtBQUNuRCxxREFBaUU7QUFDakUscURBSWlDO0FBRWpDOzs7Ozs7O0dBT0c7QUFDSSxLQUFLLFVBQVUsR0FBRyxDQUFDLEdBQWtCLEVBQUUsR0FBbUI7SUFDL0QsTUFBTSxhQUFhLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsZUFBTyxDQUFDLE9BQU8sQ0FBQyxDQUFBO0lBQ3hELE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxLQUFnQyxDQUFBO0lBRWxELG1FQUFtRTtJQUNuRSxxRUFBcUU7SUFDckUsTUFBTSxTQUFTLEdBQUcsT0FBTyxLQUFLLENBQUMsS0FBSyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFBO0lBQzNFLE1BQU0sS0FBSyxHQUFHLHNCQUFhLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxLQUFLLFNBQVMsQ0FBQztRQUN6RCxDQUFDLENBQUUsU0FBeUI7UUFDNUIsQ0FBQyxDQUFDLFNBQVMsQ0FBQTtJQUViLE1BQU0sT0FBTyxHQUNYLE9BQU8sS0FBSyxDQUFDLE9BQU8sS0FBSyxRQUFRLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUU7UUFDdkQsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFO1FBQ3RCLENBQUMsQ0FBQyxTQUFTLENBQUE7SUFFZixNQUFNLFFBQVEsR0FBRyxNQUFNLGFBQWEsQ0FBQyxZQUFZLENBQy9DLEVBQUUsRUFDRixFQUFFLFNBQVMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsQ0FDdkMsQ0FBQTtJQUVELE1BQU0sS0FBSyxHQUFHLFFBQVE7U0FDbkIsR0FBRyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUU7UUFDZixNQUFNLE9BQU8sR0FBRyxJQUFBLCtCQUFzQixFQUFDLE9BQU8sQ0FBQyxRQUFRLElBQUksRUFBRSxDQUFDLENBQUE7UUFDOUQsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2IsK0RBQStEO1lBQy9ELHNFQUFzRTtZQUN0RSxPQUFPLElBQUksQ0FBQTtRQUNiLENBQUM7UUFFRCxNQUFNLE9BQU8sR0FBRyxJQUFBLDRCQUFtQixFQUFDLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQTtRQUN4RSxJQUFJLE9BQU8sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDekIsT0FBTyxJQUFJLENBQUE7UUFDYixDQUFDO1FBRUQsT0FBTztZQUNMLFVBQVUsRUFBRSxPQUFPLENBQUMsRUFBRTtZQUN0QixLQUFLLEVBQUUsT0FBTyxDQUFDLEtBQUs7WUFDcEIsYUFBYSxFQUFFLE9BQU8sQ0FBQyxhQUFhO1lBQ3BDLE9BQU87U0FDUixDQUFBO0lBQ0gsQ0FBQyxDQUFDO1NBQ0QsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUE4QixFQUFFLENBQUMsQ0FBQyxLQUFLLElBQUksQ0FBQyxDQUFBO0lBRXhELEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDO1FBQ25CLFFBQVEsRUFBRSxLQUFLO1FBQ2YsTUFBTSxFQUFFLHNCQUFhLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBQ2hFLEtBQUssRUFBRSxLQUFLLElBQUksc0JBQWEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFO1FBQ25DLE9BQU87S0FDUixDQUFDLENBQUE7QUFDSixDQUFDIn0=
|