medusa-product-helper 0.0.19 → 0.0.21
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 +454 -16
- package/.medusa/server/src/admin/index.mjs +454 -16
- package/.medusa/server/src/api/admin/product-metadata-config/route.js +5 -2
- package/.medusa/server/src/config/product-helper-options.js +11 -1
- package/.medusa/server/src/shared/product-metadata/utils.js +6 -2
- package/package.json +1 -1
|
@@ -103,7 +103,9 @@ function coerceMetadataValue(descriptor, value) {
|
|
|
103
103
|
const num = typeof value === "number" ? value : Number(String(value).trim());
|
|
104
104
|
return isNaN(num) ? void 0 : num;
|
|
105
105
|
}
|
|
106
|
-
|
|
106
|
+
const trimmed = String(value).trim();
|
|
107
|
+
if (!trimmed) return void 0;
|
|
108
|
+
return trimmed;
|
|
107
109
|
}
|
|
108
110
|
function normalizeKey(value) {
|
|
109
111
|
return typeof value === "string" ? value.trim() || void 0 : void 0;
|
|
@@ -146,7 +148,8 @@ const useMetadataConfig = (entity) => {
|
|
|
146
148
|
};
|
|
147
149
|
const useProductMetadataConfig = () => useMetadataConfig("product");
|
|
148
150
|
const useCategoryMetadataConfig = () => useMetadataConfig("category");
|
|
149
|
-
const
|
|
151
|
+
const useOrderMetadataConfig = () => useMetadataConfig("order");
|
|
152
|
+
const CONFIG_DOCS_URL$2 = "https://docs.medusajs.com/admin/extension-points/widgets#product-category-details";
|
|
150
153
|
const CategoryMetadataTableWidget = ({ data }) => {
|
|
151
154
|
const { data: descriptors = [], isPending, isError } = useCategoryMetadataConfig();
|
|
152
155
|
const metadata = (data == null ? void 0 : data.metadata) ?? {};
|
|
@@ -258,7 +261,7 @@ const CategoryMetadataTableWidget = ({ data }) => {
|
|
|
258
261
|
"a",
|
|
259
262
|
{
|
|
260
263
|
className: "text-ui-fg-interactive underline",
|
|
261
|
-
href: CONFIG_DOCS_URL$
|
|
264
|
+
href: CONFIG_DOCS_URL$2,
|
|
262
265
|
target: "_blank",
|
|
263
266
|
rel: "noreferrer",
|
|
264
267
|
children: "Learn how to configure it."
|
|
@@ -301,7 +304,7 @@ const CategoryMetadataTableWidget = ({ data }) => {
|
|
|
301
304
|
),
|
|
302
305
|
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "align-top px-4 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
303
306
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
304
|
-
ValueField$
|
|
307
|
+
ValueField$2,
|
|
305
308
|
{
|
|
306
309
|
descriptor,
|
|
307
310
|
value,
|
|
@@ -342,7 +345,7 @@ const CategoryMetadataTableWidget = ({ data }) => {
|
|
|
342
345
|
] })
|
|
343
346
|
] });
|
|
344
347
|
};
|
|
345
|
-
const ValueField$
|
|
348
|
+
const ValueField$2 = ({
|
|
346
349
|
descriptor,
|
|
347
350
|
value,
|
|
348
351
|
onStringChange,
|
|
@@ -593,26 +596,448 @@ const HideDefaultMetadataWidget = () => {
|
|
|
593
596
|
adminSdk.defineWidgetConfig({
|
|
594
597
|
zone: "product.details.side.before"
|
|
595
598
|
});
|
|
599
|
+
const CONFIG_DOCS_URL$1 = "https://docs.medusajs.com/admin/extension-points/widgets#order-details";
|
|
600
|
+
const OrderMetadataTableWidget = ({ data }) => {
|
|
601
|
+
const { data: descriptors = [], isPending, isError } = useOrderMetadataConfig();
|
|
602
|
+
const orderId = (data == null ? void 0 : data.id) ?? void 0;
|
|
603
|
+
const [baselineMetadata, setBaselineMetadata] = react.useState(
|
|
604
|
+
(data == null ? void 0 : data.metadata) ?? {}
|
|
605
|
+
);
|
|
606
|
+
const queryClient = reactQuery.useQueryClient();
|
|
607
|
+
const previousOrderIdRef = react.useRef(orderId);
|
|
608
|
+
const isInitializedRef = react.useRef(false);
|
|
609
|
+
const dataRef = react.useRef(data);
|
|
610
|
+
const descriptorsRef = react.useRef(descriptors);
|
|
611
|
+
react.useEffect(() => {
|
|
612
|
+
dataRef.current = data;
|
|
613
|
+
descriptorsRef.current = descriptors;
|
|
614
|
+
}, [data, descriptors]);
|
|
615
|
+
react.useEffect(() => {
|
|
616
|
+
var _a;
|
|
617
|
+
if (previousOrderIdRef.current === orderId && isInitializedRef.current) {
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
const orderIdChanged = previousOrderIdRef.current !== orderId;
|
|
621
|
+
if (orderIdChanged || !isInitializedRef.current) {
|
|
622
|
+
const currentMetadata = (data == null ? void 0 : data.metadata) ?? ((_a = dataRef.current) == null ? void 0 : _a.metadata) ?? {};
|
|
623
|
+
const currentDescriptors = descriptorsRef.current.length > 0 ? descriptorsRef.current : descriptors;
|
|
624
|
+
if (currentDescriptors.length === 0) {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
previousOrderIdRef.current = orderId;
|
|
628
|
+
setBaselineMetadata(currentMetadata);
|
|
629
|
+
const newInitialState = buildInitialFormState(currentDescriptors, currentMetadata);
|
|
630
|
+
setValues(newInitialState);
|
|
631
|
+
isInitializedRef.current = true;
|
|
632
|
+
}
|
|
633
|
+
}, [orderId]);
|
|
634
|
+
const metadataStringRef = react.useRef("");
|
|
635
|
+
react.useEffect(() => {
|
|
636
|
+
const hasMetadata = (data == null ? void 0 : data.metadata) && Object.keys(data.metadata).length > 0;
|
|
637
|
+
const descriptorsLoaded = descriptors.length > 0;
|
|
638
|
+
const sameOrder = previousOrderIdRef.current === orderId;
|
|
639
|
+
const notInitialized = !isInitializedRef.current;
|
|
640
|
+
const currentMetadataString = hasMetadata ? JSON.stringify(data.metadata) : "";
|
|
641
|
+
const metadataChanged = currentMetadataString !== metadataStringRef.current;
|
|
642
|
+
if (hasMetadata && descriptorsLoaded && sameOrder && (notInitialized || metadataChanged)) {
|
|
643
|
+
const currentMetadata = data.metadata ?? {};
|
|
644
|
+
const newInitialState = buildInitialFormState(descriptors, currentMetadata);
|
|
645
|
+
setBaselineMetadata(currentMetadata);
|
|
646
|
+
setValues(newInitialState);
|
|
647
|
+
metadataStringRef.current = currentMetadataString;
|
|
648
|
+
isInitializedRef.current = true;
|
|
649
|
+
}
|
|
650
|
+
}, [data, descriptors.length, orderId]);
|
|
651
|
+
const initialState = react.useMemo(
|
|
652
|
+
() => buildInitialFormState(descriptors, baselineMetadata),
|
|
653
|
+
[descriptors, baselineMetadata]
|
|
654
|
+
);
|
|
655
|
+
const [values, setValues] = react.useState({});
|
|
656
|
+
const [isSaving, setIsSaving] = react.useState(false);
|
|
657
|
+
const errors = react.useMemo(() => {
|
|
658
|
+
return descriptors.reduce((acc, descriptor) => {
|
|
659
|
+
const error = validateValueForDescriptor(descriptor, values[descriptor.key]);
|
|
660
|
+
if (error) {
|
|
661
|
+
acc[descriptor.key] = error;
|
|
662
|
+
}
|
|
663
|
+
return acc;
|
|
664
|
+
}, {});
|
|
665
|
+
}, [descriptors, values]);
|
|
666
|
+
const hasErrors = Object.keys(errors).length > 0;
|
|
667
|
+
const isDirty = react.useMemo(() => {
|
|
668
|
+
return hasMetadataChanges({
|
|
669
|
+
descriptors,
|
|
670
|
+
values,
|
|
671
|
+
originalMetadata: baselineMetadata
|
|
672
|
+
});
|
|
673
|
+
}, [descriptors, values, baselineMetadata]);
|
|
674
|
+
const handleStringChange = (key, nextValue) => {
|
|
675
|
+
setValues((prev) => ({
|
|
676
|
+
...prev,
|
|
677
|
+
[key]: nextValue
|
|
678
|
+
}));
|
|
679
|
+
};
|
|
680
|
+
const handleBooleanChange = (key, nextValue) => {
|
|
681
|
+
setValues((prev) => ({
|
|
682
|
+
...prev,
|
|
683
|
+
[key]: nextValue
|
|
684
|
+
}));
|
|
685
|
+
};
|
|
686
|
+
const handleReset = () => {
|
|
687
|
+
setValues(initialState);
|
|
688
|
+
};
|
|
689
|
+
const handleSubmit = async () => {
|
|
690
|
+
if (!(data == null ? void 0 : data.id) || !descriptors.length) {
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
setIsSaving(true);
|
|
694
|
+
try {
|
|
695
|
+
const metadataPayload = buildMetadataPayload({
|
|
696
|
+
descriptors,
|
|
697
|
+
values,
|
|
698
|
+
originalMetadata: baselineMetadata
|
|
699
|
+
});
|
|
700
|
+
const response = await fetch(`/admin/orders/${data.id}`, {
|
|
701
|
+
method: "POST",
|
|
702
|
+
credentials: "include",
|
|
703
|
+
headers: {
|
|
704
|
+
"Content-Type": "application/json"
|
|
705
|
+
},
|
|
706
|
+
body: JSON.stringify({
|
|
707
|
+
metadata: metadataPayload
|
|
708
|
+
})
|
|
709
|
+
});
|
|
710
|
+
if (!response.ok) {
|
|
711
|
+
const payload = await response.json().catch(() => null);
|
|
712
|
+
throw new Error((payload == null ? void 0 : payload.message) ?? "Unable to save metadata");
|
|
713
|
+
}
|
|
714
|
+
const updated = await response.json();
|
|
715
|
+
const nextMetadata = updated.order.metadata;
|
|
716
|
+
setBaselineMetadata(nextMetadata);
|
|
717
|
+
setValues(buildInitialFormState(descriptors, nextMetadata));
|
|
718
|
+
ui.toast.success("Metadata saved");
|
|
719
|
+
await queryClient.invalidateQueries({
|
|
720
|
+
queryKey: ["orders"]
|
|
721
|
+
});
|
|
722
|
+
await queryClient.invalidateQueries({
|
|
723
|
+
queryKey: ["order", data.id]
|
|
724
|
+
});
|
|
725
|
+
if (data.id) {
|
|
726
|
+
queryClient.refetchQueries({
|
|
727
|
+
queryKey: ["order", data.id]
|
|
728
|
+
}).catch(() => {
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
} catch (error) {
|
|
732
|
+
ui.toast.error(error instanceof Error ? error.message : "Save failed");
|
|
733
|
+
} finally {
|
|
734
|
+
setIsSaving(false);
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "flex flex-col gap-y-4", children: [
|
|
738
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex flex-col gap-y-1", children: [
|
|
739
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
|
|
740
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Metadata" }),
|
|
741
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", rounded: "full", children: descriptors.length })
|
|
742
|
+
] }),
|
|
743
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Structured metadata mapped to the keys you configured in the plugin options." })
|
|
744
|
+
] }),
|
|
745
|
+
isPending || !isInitializedRef.current || Object.keys(values).length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-[160px] w-full" }) : isError ? /* @__PURE__ */ jsxRuntime.jsxs(ui.InlineTip, { variant: "error", label: "Configuration unavailable", children: [
|
|
746
|
+
"Unable to load metadata configuration for this plugin. Confirm that the plugin is registered with options in ",
|
|
747
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "medusa-config.ts" }),
|
|
748
|
+
"."
|
|
749
|
+
] }) : !descriptors.length ? /* @__PURE__ */ jsxRuntime.jsxs(ui.InlineTip, { variant: "info", label: "No configured metadata keys", children: [
|
|
750
|
+
"Provide a ",
|
|
751
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "metadataDescriptors" }),
|
|
752
|
+
" array in the plugin options to control which keys show up here.",
|
|
753
|
+
" ",
|
|
754
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
755
|
+
"a",
|
|
756
|
+
{
|
|
757
|
+
className: "text-ui-fg-interactive underline",
|
|
758
|
+
href: CONFIG_DOCS_URL$1,
|
|
759
|
+
target: "_blank",
|
|
760
|
+
rel: "noreferrer",
|
|
761
|
+
children: "Learn how to configure it."
|
|
762
|
+
}
|
|
763
|
+
)
|
|
764
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
765
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden rounded-lg border border-ui-border-base", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "min-w-full divide-y divide-ui-border-base", children: [
|
|
766
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
767
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
768
|
+
"th",
|
|
769
|
+
{
|
|
770
|
+
scope: "col",
|
|
771
|
+
className: "txt-compact-xsmall-plus text-left uppercase tracking-wide text-ui-fg-muted px-4 py-3",
|
|
772
|
+
children: "Label"
|
|
773
|
+
}
|
|
774
|
+
),
|
|
775
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
776
|
+
"th",
|
|
777
|
+
{
|
|
778
|
+
scope: "col",
|
|
779
|
+
className: "txt-compact-xsmall-plus text-left uppercase tracking-wide text-ui-fg-muted px-4 py-3",
|
|
780
|
+
children: "Value"
|
|
781
|
+
}
|
|
782
|
+
)
|
|
783
|
+
] }) }),
|
|
784
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-ui-border-subtle bg-ui-bg-base", children: descriptors.map((descriptor) => {
|
|
785
|
+
const value = values[descriptor.key];
|
|
786
|
+
const error = errors[descriptor.key];
|
|
787
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
788
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
789
|
+
"th",
|
|
790
|
+
{
|
|
791
|
+
scope: "row",
|
|
792
|
+
className: "txt-compact-medium text-ui-fg-base align-top px-4 py-4",
|
|
793
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
794
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: descriptor.label ?? descriptor.key }),
|
|
795
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-compact-xsmall-plus text-ui-fg-muted uppercase tracking-wide", children: descriptor.type })
|
|
796
|
+
] })
|
|
797
|
+
}
|
|
798
|
+
),
|
|
799
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "align-top px-4 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
800
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
801
|
+
ValueField$1,
|
|
802
|
+
{
|
|
803
|
+
descriptor,
|
|
804
|
+
value,
|
|
805
|
+
onStringChange: handleStringChange,
|
|
806
|
+
onBooleanChange: handleBooleanChange
|
|
807
|
+
}
|
|
808
|
+
),
|
|
809
|
+
error && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "txt-compact-small text-ui-fg-error", children: error })
|
|
810
|
+
] }) })
|
|
811
|
+
] }, descriptor.key);
|
|
812
|
+
}) })
|
|
813
|
+
] }) }),
|
|
814
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3 border-t border-ui-border-subtle pt-3 md:flex-row md:items-center md:justify-between", children: [
|
|
815
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "Changes are stored on the order metadata object. Clearing a field removes the corresponding key on save." }),
|
|
816
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
817
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
818
|
+
ui.Button,
|
|
819
|
+
{
|
|
820
|
+
variant: "secondary",
|
|
821
|
+
size: "small",
|
|
822
|
+
disabled: !isDirty || isSaving,
|
|
823
|
+
onClick: handleReset,
|
|
824
|
+
children: "Reset"
|
|
825
|
+
}
|
|
826
|
+
),
|
|
827
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
828
|
+
ui.Button,
|
|
829
|
+
{
|
|
830
|
+
size: "small",
|
|
831
|
+
onClick: handleSubmit,
|
|
832
|
+
disabled: !isDirty || hasErrors || isSaving,
|
|
833
|
+
isLoading: isSaving,
|
|
834
|
+
children: "Save metadata"
|
|
835
|
+
}
|
|
836
|
+
)
|
|
837
|
+
] })
|
|
838
|
+
] })
|
|
839
|
+
] })
|
|
840
|
+
] });
|
|
841
|
+
};
|
|
842
|
+
const ValueField$1 = ({
|
|
843
|
+
descriptor,
|
|
844
|
+
value,
|
|
845
|
+
onStringChange,
|
|
846
|
+
onBooleanChange
|
|
847
|
+
}) => {
|
|
848
|
+
const fileInputRef = react.useRef(null);
|
|
849
|
+
const [isUploading, setIsUploading] = react.useState(false);
|
|
850
|
+
const handleFileUpload = async (event) => {
|
|
851
|
+
var _a;
|
|
852
|
+
const file = (_a = event.target.files) == null ? void 0 : _a[0];
|
|
853
|
+
if (!file) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
setIsUploading(true);
|
|
857
|
+
try {
|
|
858
|
+
const formData = new FormData();
|
|
859
|
+
formData.append("files", file);
|
|
860
|
+
const response = await fetch("/admin/uploads", {
|
|
861
|
+
method: "POST",
|
|
862
|
+
credentials: "include",
|
|
863
|
+
body: formData
|
|
864
|
+
});
|
|
865
|
+
if (!response.ok) {
|
|
866
|
+
const payload = await response.json().catch(() => null);
|
|
867
|
+
throw new Error((payload == null ? void 0 : payload.message) ?? "File upload failed");
|
|
868
|
+
}
|
|
869
|
+
const result = await response.json();
|
|
870
|
+
if (result.files && result.files.length > 0) {
|
|
871
|
+
const uploadedFile = result.files[0];
|
|
872
|
+
const fileUrl = uploadedFile.url || uploadedFile.key;
|
|
873
|
+
if (fileUrl) {
|
|
874
|
+
onStringChange(descriptor.key, fileUrl);
|
|
875
|
+
ui.toast.success("File uploaded successfully");
|
|
876
|
+
} else {
|
|
877
|
+
throw new Error("File upload succeeded but no URL returned");
|
|
878
|
+
}
|
|
879
|
+
} else {
|
|
880
|
+
throw new Error("File upload failed - no files returned");
|
|
881
|
+
}
|
|
882
|
+
} catch (error) {
|
|
883
|
+
ui.toast.error(
|
|
884
|
+
error instanceof Error ? error.message : "Failed to upload file"
|
|
885
|
+
);
|
|
886
|
+
} finally {
|
|
887
|
+
setIsUploading(false);
|
|
888
|
+
if (fileInputRef.current) {
|
|
889
|
+
fileInputRef.current.value = "";
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
if (descriptor.type === "bool") {
|
|
894
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
895
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
896
|
+
ui.Switch,
|
|
897
|
+
{
|
|
898
|
+
checked: Boolean(value),
|
|
899
|
+
onCheckedChange: (checked) => onBooleanChange(descriptor.key, Boolean(checked)),
|
|
900
|
+
"aria-label": `Toggle ${descriptor.label ?? descriptor.key}`
|
|
901
|
+
}
|
|
902
|
+
),
|
|
903
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "txt-compact-small text-ui-fg-muted", children: Boolean(value) ? "True" : "False" })
|
|
904
|
+
] });
|
|
905
|
+
}
|
|
906
|
+
if (descriptor.type === "text") {
|
|
907
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
908
|
+
ui.Textarea,
|
|
909
|
+
{
|
|
910
|
+
value: value ?? "",
|
|
911
|
+
placeholder: "Enter text",
|
|
912
|
+
rows: 3,
|
|
913
|
+
onChange: (event) => onStringChange(descriptor.key, event.target.value)
|
|
914
|
+
}
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
if (descriptor.type === "number") {
|
|
918
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
919
|
+
ui.Input,
|
|
920
|
+
{
|
|
921
|
+
type: "text",
|
|
922
|
+
inputMode: "decimal",
|
|
923
|
+
placeholder: "0.00",
|
|
924
|
+
value: value ?? "",
|
|
925
|
+
onChange: (event) => onStringChange(descriptor.key, event.target.value)
|
|
926
|
+
}
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
930
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
931
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
932
|
+
ui.Input,
|
|
933
|
+
{
|
|
934
|
+
type: "url",
|
|
935
|
+
placeholder: "https://example.com/file",
|
|
936
|
+
value: value ?? "",
|
|
937
|
+
onChange: (event) => onStringChange(descriptor.key, event.target.value),
|
|
938
|
+
className: "flex-1"
|
|
939
|
+
}
|
|
940
|
+
),
|
|
941
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
942
|
+
"input",
|
|
943
|
+
{
|
|
944
|
+
ref: fileInputRef,
|
|
945
|
+
type: "file",
|
|
946
|
+
className: "hidden",
|
|
947
|
+
onChange: handleFileUpload,
|
|
948
|
+
disabled: isUploading,
|
|
949
|
+
"aria-label": `Upload file for ${descriptor.label ?? descriptor.key}`
|
|
950
|
+
}
|
|
951
|
+
),
|
|
952
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
953
|
+
ui.Button,
|
|
954
|
+
{
|
|
955
|
+
type: "button",
|
|
956
|
+
variant: "secondary",
|
|
957
|
+
size: "small",
|
|
958
|
+
onClick: () => {
|
|
959
|
+
var _a;
|
|
960
|
+
return (_a = fileInputRef.current) == null ? void 0 : _a.click();
|
|
961
|
+
},
|
|
962
|
+
disabled: isUploading,
|
|
963
|
+
isLoading: isUploading,
|
|
964
|
+
children: isUploading ? "Uploading..." : "Upload"
|
|
965
|
+
}
|
|
966
|
+
)
|
|
967
|
+
] }),
|
|
968
|
+
typeof value === "string" && value && /* @__PURE__ */ jsxRuntime.jsx(
|
|
969
|
+
"a",
|
|
970
|
+
{
|
|
971
|
+
className: "txt-compact-small-plus text-ui-fg-interactive underline",
|
|
972
|
+
href: value,
|
|
973
|
+
target: "_blank",
|
|
974
|
+
rel: "noreferrer",
|
|
975
|
+
children: "View file"
|
|
976
|
+
}
|
|
977
|
+
)
|
|
978
|
+
] });
|
|
979
|
+
};
|
|
980
|
+
adminSdk.defineWidgetConfig({
|
|
981
|
+
zone: "order.details.after"
|
|
982
|
+
});
|
|
596
983
|
const CONFIG_DOCS_URL = "https://docs.medusajs.com/admin/extension-points/widgets#product-details";
|
|
597
984
|
const ProductMetadataTableWidget = ({ data }) => {
|
|
598
985
|
const { data: descriptors = [], isPending, isError } = useProductMetadataConfig();
|
|
599
|
-
const
|
|
600
|
-
const [baselineMetadata, setBaselineMetadata] = react.useState(
|
|
986
|
+
const productId = (data == null ? void 0 : data.id) ?? void 0;
|
|
987
|
+
const [baselineMetadata, setBaselineMetadata] = react.useState(
|
|
988
|
+
(data == null ? void 0 : data.metadata) ?? {}
|
|
989
|
+
);
|
|
601
990
|
const queryClient = reactQuery.useQueryClient();
|
|
991
|
+
const previousProductIdRef = react.useRef(productId);
|
|
992
|
+
const isInitializedRef = react.useRef(false);
|
|
993
|
+
const dataRef = react.useRef(data);
|
|
994
|
+
const descriptorsRef = react.useRef(descriptors);
|
|
602
995
|
react.useEffect(() => {
|
|
603
|
-
|
|
604
|
-
|
|
996
|
+
dataRef.current = data;
|
|
997
|
+
descriptorsRef.current = descriptors;
|
|
998
|
+
}, [data, descriptors]);
|
|
999
|
+
react.useEffect(() => {
|
|
1000
|
+
var _a;
|
|
1001
|
+
if (previousProductIdRef.current === productId && isInitializedRef.current) {
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
const productIdChanged = previousProductIdRef.current !== productId;
|
|
1005
|
+
if (productIdChanged || !isInitializedRef.current) {
|
|
1006
|
+
const currentMetadata = (data == null ? void 0 : data.metadata) ?? ((_a = dataRef.current) == null ? void 0 : _a.metadata) ?? {};
|
|
1007
|
+
const currentDescriptors = descriptorsRef.current.length > 0 ? descriptorsRef.current : descriptors;
|
|
1008
|
+
if (currentDescriptors.length === 0) {
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
previousProductIdRef.current = productId;
|
|
1012
|
+
setBaselineMetadata(currentMetadata);
|
|
1013
|
+
const newInitialState = buildInitialFormState(currentDescriptors, currentMetadata);
|
|
1014
|
+
setValues(newInitialState);
|
|
1015
|
+
isInitializedRef.current = true;
|
|
1016
|
+
}
|
|
1017
|
+
}, [productId]);
|
|
1018
|
+
const metadataStringRef = react.useRef("");
|
|
1019
|
+
react.useEffect(() => {
|
|
1020
|
+
const hasMetadata = (data == null ? void 0 : data.metadata) && Object.keys(data.metadata).length > 0;
|
|
1021
|
+
const descriptorsLoaded = descriptors.length > 0;
|
|
1022
|
+
const sameProduct = previousProductIdRef.current === productId;
|
|
1023
|
+
const notInitialized = !isInitializedRef.current;
|
|
1024
|
+
const currentMetadataString = hasMetadata ? JSON.stringify(data.metadata) : "";
|
|
1025
|
+
const metadataChanged = currentMetadataString !== metadataStringRef.current;
|
|
1026
|
+
if (hasMetadata && descriptorsLoaded && sameProduct && (notInitialized || metadataChanged)) {
|
|
1027
|
+
const currentMetadata = data.metadata ?? {};
|
|
1028
|
+
const newInitialState = buildInitialFormState(descriptors, currentMetadata);
|
|
1029
|
+
setBaselineMetadata(currentMetadata);
|
|
1030
|
+
setValues(newInitialState);
|
|
1031
|
+
metadataStringRef.current = currentMetadataString;
|
|
1032
|
+
isInitializedRef.current = true;
|
|
1033
|
+
}
|
|
1034
|
+
}, [data, descriptors.length, productId]);
|
|
605
1035
|
const initialState = react.useMemo(
|
|
606
1036
|
() => buildInitialFormState(descriptors, baselineMetadata),
|
|
607
1037
|
[descriptors, baselineMetadata]
|
|
608
1038
|
);
|
|
609
|
-
const [values, setValues] = react.useState(
|
|
610
|
-
initialState
|
|
611
|
-
);
|
|
1039
|
+
const [values, setValues] = react.useState({});
|
|
612
1040
|
const [isSaving, setIsSaving] = react.useState(false);
|
|
613
|
-
react.useEffect(() => {
|
|
614
|
-
setValues(initialState);
|
|
615
|
-
}, [initialState]);
|
|
616
1041
|
const errors = react.useMemo(() => {
|
|
617
1042
|
return descriptors.reduce((acc, descriptor) => {
|
|
618
1043
|
const error = validateValueForDescriptor(descriptor, values[descriptor.key]);
|
|
@@ -678,6 +1103,15 @@ const ProductMetadataTableWidget = ({ data }) => {
|
|
|
678
1103
|
await queryClient.invalidateQueries({
|
|
679
1104
|
queryKey: ["products"]
|
|
680
1105
|
});
|
|
1106
|
+
await queryClient.invalidateQueries({
|
|
1107
|
+
queryKey: ["product", data.id]
|
|
1108
|
+
});
|
|
1109
|
+
if (data.id) {
|
|
1110
|
+
queryClient.refetchQueries({
|
|
1111
|
+
queryKey: ["product", data.id]
|
|
1112
|
+
}).catch(() => {
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
681
1115
|
} catch (error) {
|
|
682
1116
|
ui.toast.error(error instanceof Error ? error.message : "Save failed");
|
|
683
1117
|
} finally {
|
|
@@ -692,7 +1126,7 @@ const ProductMetadataTableWidget = ({ data }) => {
|
|
|
692
1126
|
] }),
|
|
693
1127
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Structured metadata mapped to the keys you configured in the plugin options." })
|
|
694
1128
|
] }),
|
|
695
|
-
isPending ? /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-[160px] w-full" }) : isError ? /* @__PURE__ */ jsxRuntime.jsxs(ui.InlineTip, { variant: "error", label: "Configuration unavailable", children: [
|
|
1129
|
+
isPending || !isInitializedRef.current || Object.keys(values).length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-[160px] w-full" }) : isError ? /* @__PURE__ */ jsxRuntime.jsxs(ui.InlineTip, { variant: "error", label: "Configuration unavailable", children: [
|
|
696
1130
|
"Unable to load metadata configuration for this plugin. Confirm that the plugin is registered with options in ",
|
|
697
1131
|
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "medusa-config.ts" }),
|
|
698
1132
|
"."
|
|
@@ -1067,6 +1501,10 @@ const widgetModule = { widgets: [
|
|
|
1067
1501
|
Component: HideDefaultMetadataWidget,
|
|
1068
1502
|
zone: ["product.details.side.before"]
|
|
1069
1503
|
},
|
|
1504
|
+
{
|
|
1505
|
+
Component: OrderMetadataTableWidget,
|
|
1506
|
+
zone: ["order.details.after"]
|
|
1507
|
+
},
|
|
1070
1508
|
{
|
|
1071
1509
|
Component: ProductMetadataTableWidget,
|
|
1072
1510
|
zone: ["product.details.after"]
|
|
@@ -102,7 +102,9 @@ function coerceMetadataValue(descriptor, value) {
|
|
|
102
102
|
const num = typeof value === "number" ? value : Number(String(value).trim());
|
|
103
103
|
return isNaN(num) ? void 0 : num;
|
|
104
104
|
}
|
|
105
|
-
|
|
105
|
+
const trimmed = String(value).trim();
|
|
106
|
+
if (!trimmed) return void 0;
|
|
107
|
+
return trimmed;
|
|
106
108
|
}
|
|
107
109
|
function normalizeKey(value) {
|
|
108
110
|
return typeof value === "string" ? value.trim() || void 0 : void 0;
|
|
@@ -145,7 +147,8 @@ const useMetadataConfig = (entity) => {
|
|
|
145
147
|
};
|
|
146
148
|
const useProductMetadataConfig = () => useMetadataConfig("product");
|
|
147
149
|
const useCategoryMetadataConfig = () => useMetadataConfig("category");
|
|
148
|
-
const
|
|
150
|
+
const useOrderMetadataConfig = () => useMetadataConfig("order");
|
|
151
|
+
const CONFIG_DOCS_URL$2 = "https://docs.medusajs.com/admin/extension-points/widgets#product-category-details";
|
|
149
152
|
const CategoryMetadataTableWidget = ({ data }) => {
|
|
150
153
|
const { data: descriptors = [], isPending, isError } = useCategoryMetadataConfig();
|
|
151
154
|
const metadata = (data == null ? void 0 : data.metadata) ?? {};
|
|
@@ -257,7 +260,7 @@ const CategoryMetadataTableWidget = ({ data }) => {
|
|
|
257
260
|
"a",
|
|
258
261
|
{
|
|
259
262
|
className: "text-ui-fg-interactive underline",
|
|
260
|
-
href: CONFIG_DOCS_URL$
|
|
263
|
+
href: CONFIG_DOCS_URL$2,
|
|
261
264
|
target: "_blank",
|
|
262
265
|
rel: "noreferrer",
|
|
263
266
|
children: "Learn how to configure it."
|
|
@@ -300,7 +303,7 @@ const CategoryMetadataTableWidget = ({ data }) => {
|
|
|
300
303
|
),
|
|
301
304
|
/* @__PURE__ */ jsx("td", { className: "align-top px-4 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
302
305
|
/* @__PURE__ */ jsx(
|
|
303
|
-
ValueField$
|
|
306
|
+
ValueField$2,
|
|
304
307
|
{
|
|
305
308
|
descriptor,
|
|
306
309
|
value,
|
|
@@ -341,7 +344,7 @@ const CategoryMetadataTableWidget = ({ data }) => {
|
|
|
341
344
|
] })
|
|
342
345
|
] });
|
|
343
346
|
};
|
|
344
|
-
const ValueField$
|
|
347
|
+
const ValueField$2 = ({
|
|
345
348
|
descriptor,
|
|
346
349
|
value,
|
|
347
350
|
onStringChange,
|
|
@@ -592,26 +595,448 @@ const HideDefaultMetadataWidget = () => {
|
|
|
592
595
|
defineWidgetConfig({
|
|
593
596
|
zone: "product.details.side.before"
|
|
594
597
|
});
|
|
598
|
+
const CONFIG_DOCS_URL$1 = "https://docs.medusajs.com/admin/extension-points/widgets#order-details";
|
|
599
|
+
const OrderMetadataTableWidget = ({ data }) => {
|
|
600
|
+
const { data: descriptors = [], isPending, isError } = useOrderMetadataConfig();
|
|
601
|
+
const orderId = (data == null ? void 0 : data.id) ?? void 0;
|
|
602
|
+
const [baselineMetadata, setBaselineMetadata] = useState(
|
|
603
|
+
(data == null ? void 0 : data.metadata) ?? {}
|
|
604
|
+
);
|
|
605
|
+
const queryClient = useQueryClient();
|
|
606
|
+
const previousOrderIdRef = useRef(orderId);
|
|
607
|
+
const isInitializedRef = useRef(false);
|
|
608
|
+
const dataRef = useRef(data);
|
|
609
|
+
const descriptorsRef = useRef(descriptors);
|
|
610
|
+
useEffect(() => {
|
|
611
|
+
dataRef.current = data;
|
|
612
|
+
descriptorsRef.current = descriptors;
|
|
613
|
+
}, [data, descriptors]);
|
|
614
|
+
useEffect(() => {
|
|
615
|
+
var _a;
|
|
616
|
+
if (previousOrderIdRef.current === orderId && isInitializedRef.current) {
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
const orderIdChanged = previousOrderIdRef.current !== orderId;
|
|
620
|
+
if (orderIdChanged || !isInitializedRef.current) {
|
|
621
|
+
const currentMetadata = (data == null ? void 0 : data.metadata) ?? ((_a = dataRef.current) == null ? void 0 : _a.metadata) ?? {};
|
|
622
|
+
const currentDescriptors = descriptorsRef.current.length > 0 ? descriptorsRef.current : descriptors;
|
|
623
|
+
if (currentDescriptors.length === 0) {
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
previousOrderIdRef.current = orderId;
|
|
627
|
+
setBaselineMetadata(currentMetadata);
|
|
628
|
+
const newInitialState = buildInitialFormState(currentDescriptors, currentMetadata);
|
|
629
|
+
setValues(newInitialState);
|
|
630
|
+
isInitializedRef.current = true;
|
|
631
|
+
}
|
|
632
|
+
}, [orderId]);
|
|
633
|
+
const metadataStringRef = useRef("");
|
|
634
|
+
useEffect(() => {
|
|
635
|
+
const hasMetadata = (data == null ? void 0 : data.metadata) && Object.keys(data.metadata).length > 0;
|
|
636
|
+
const descriptorsLoaded = descriptors.length > 0;
|
|
637
|
+
const sameOrder = previousOrderIdRef.current === orderId;
|
|
638
|
+
const notInitialized = !isInitializedRef.current;
|
|
639
|
+
const currentMetadataString = hasMetadata ? JSON.stringify(data.metadata) : "";
|
|
640
|
+
const metadataChanged = currentMetadataString !== metadataStringRef.current;
|
|
641
|
+
if (hasMetadata && descriptorsLoaded && sameOrder && (notInitialized || metadataChanged)) {
|
|
642
|
+
const currentMetadata = data.metadata ?? {};
|
|
643
|
+
const newInitialState = buildInitialFormState(descriptors, currentMetadata);
|
|
644
|
+
setBaselineMetadata(currentMetadata);
|
|
645
|
+
setValues(newInitialState);
|
|
646
|
+
metadataStringRef.current = currentMetadataString;
|
|
647
|
+
isInitializedRef.current = true;
|
|
648
|
+
}
|
|
649
|
+
}, [data, descriptors.length, orderId]);
|
|
650
|
+
const initialState = useMemo(
|
|
651
|
+
() => buildInitialFormState(descriptors, baselineMetadata),
|
|
652
|
+
[descriptors, baselineMetadata]
|
|
653
|
+
);
|
|
654
|
+
const [values, setValues] = useState({});
|
|
655
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
656
|
+
const errors = useMemo(() => {
|
|
657
|
+
return descriptors.reduce((acc, descriptor) => {
|
|
658
|
+
const error = validateValueForDescriptor(descriptor, values[descriptor.key]);
|
|
659
|
+
if (error) {
|
|
660
|
+
acc[descriptor.key] = error;
|
|
661
|
+
}
|
|
662
|
+
return acc;
|
|
663
|
+
}, {});
|
|
664
|
+
}, [descriptors, values]);
|
|
665
|
+
const hasErrors = Object.keys(errors).length > 0;
|
|
666
|
+
const isDirty = useMemo(() => {
|
|
667
|
+
return hasMetadataChanges({
|
|
668
|
+
descriptors,
|
|
669
|
+
values,
|
|
670
|
+
originalMetadata: baselineMetadata
|
|
671
|
+
});
|
|
672
|
+
}, [descriptors, values, baselineMetadata]);
|
|
673
|
+
const handleStringChange = (key, nextValue) => {
|
|
674
|
+
setValues((prev) => ({
|
|
675
|
+
...prev,
|
|
676
|
+
[key]: nextValue
|
|
677
|
+
}));
|
|
678
|
+
};
|
|
679
|
+
const handleBooleanChange = (key, nextValue) => {
|
|
680
|
+
setValues((prev) => ({
|
|
681
|
+
...prev,
|
|
682
|
+
[key]: nextValue
|
|
683
|
+
}));
|
|
684
|
+
};
|
|
685
|
+
const handleReset = () => {
|
|
686
|
+
setValues(initialState);
|
|
687
|
+
};
|
|
688
|
+
const handleSubmit = async () => {
|
|
689
|
+
if (!(data == null ? void 0 : data.id) || !descriptors.length) {
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
setIsSaving(true);
|
|
693
|
+
try {
|
|
694
|
+
const metadataPayload = buildMetadataPayload({
|
|
695
|
+
descriptors,
|
|
696
|
+
values,
|
|
697
|
+
originalMetadata: baselineMetadata
|
|
698
|
+
});
|
|
699
|
+
const response = await fetch(`/admin/orders/${data.id}`, {
|
|
700
|
+
method: "POST",
|
|
701
|
+
credentials: "include",
|
|
702
|
+
headers: {
|
|
703
|
+
"Content-Type": "application/json"
|
|
704
|
+
},
|
|
705
|
+
body: JSON.stringify({
|
|
706
|
+
metadata: metadataPayload
|
|
707
|
+
})
|
|
708
|
+
});
|
|
709
|
+
if (!response.ok) {
|
|
710
|
+
const payload = await response.json().catch(() => null);
|
|
711
|
+
throw new Error((payload == null ? void 0 : payload.message) ?? "Unable to save metadata");
|
|
712
|
+
}
|
|
713
|
+
const updated = await response.json();
|
|
714
|
+
const nextMetadata = updated.order.metadata;
|
|
715
|
+
setBaselineMetadata(nextMetadata);
|
|
716
|
+
setValues(buildInitialFormState(descriptors, nextMetadata));
|
|
717
|
+
toast.success("Metadata saved");
|
|
718
|
+
await queryClient.invalidateQueries({
|
|
719
|
+
queryKey: ["orders"]
|
|
720
|
+
});
|
|
721
|
+
await queryClient.invalidateQueries({
|
|
722
|
+
queryKey: ["order", data.id]
|
|
723
|
+
});
|
|
724
|
+
if (data.id) {
|
|
725
|
+
queryClient.refetchQueries({
|
|
726
|
+
queryKey: ["order", data.id]
|
|
727
|
+
}).catch(() => {
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
} catch (error) {
|
|
731
|
+
toast.error(error instanceof Error ? error.message : "Save failed");
|
|
732
|
+
} finally {
|
|
733
|
+
setIsSaving(false);
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
return /* @__PURE__ */ jsxs(Container, { className: "flex flex-col gap-y-4", children: [
|
|
737
|
+
/* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-y-1", children: [
|
|
738
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-3", children: [
|
|
739
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: "Metadata" }),
|
|
740
|
+
/* @__PURE__ */ jsx(Badge, { size: "2xsmall", rounded: "full", children: descriptors.length })
|
|
741
|
+
] }),
|
|
742
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Structured metadata mapped to the keys you configured in the plugin options." })
|
|
743
|
+
] }),
|
|
744
|
+
isPending || !isInitializedRef.current || Object.keys(values).length === 0 ? /* @__PURE__ */ jsx(Skeleton, { className: "h-[160px] w-full" }) : isError ? /* @__PURE__ */ jsxs(InlineTip, { variant: "error", label: "Configuration unavailable", children: [
|
|
745
|
+
"Unable to load metadata configuration for this plugin. Confirm that the plugin is registered with options in ",
|
|
746
|
+
/* @__PURE__ */ jsx("code", { children: "medusa-config.ts" }),
|
|
747
|
+
"."
|
|
748
|
+
] }) : !descriptors.length ? /* @__PURE__ */ jsxs(InlineTip, { variant: "info", label: "No configured metadata keys", children: [
|
|
749
|
+
"Provide a ",
|
|
750
|
+
/* @__PURE__ */ jsx("code", { children: "metadataDescriptors" }),
|
|
751
|
+
" array in the plugin options to control which keys show up here.",
|
|
752
|
+
" ",
|
|
753
|
+
/* @__PURE__ */ jsx(
|
|
754
|
+
"a",
|
|
755
|
+
{
|
|
756
|
+
className: "text-ui-fg-interactive underline",
|
|
757
|
+
href: CONFIG_DOCS_URL$1,
|
|
758
|
+
target: "_blank",
|
|
759
|
+
rel: "noreferrer",
|
|
760
|
+
children: "Learn how to configure it."
|
|
761
|
+
}
|
|
762
|
+
)
|
|
763
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
764
|
+
/* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-lg border border-ui-border-base", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full divide-y divide-ui-border-base", children: [
|
|
765
|
+
/* @__PURE__ */ jsx("thead", { className: "bg-ui-bg-subtle", children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
766
|
+
/* @__PURE__ */ jsx(
|
|
767
|
+
"th",
|
|
768
|
+
{
|
|
769
|
+
scope: "col",
|
|
770
|
+
className: "txt-compact-xsmall-plus text-left uppercase tracking-wide text-ui-fg-muted px-4 py-3",
|
|
771
|
+
children: "Label"
|
|
772
|
+
}
|
|
773
|
+
),
|
|
774
|
+
/* @__PURE__ */ jsx(
|
|
775
|
+
"th",
|
|
776
|
+
{
|
|
777
|
+
scope: "col",
|
|
778
|
+
className: "txt-compact-xsmall-plus text-left uppercase tracking-wide text-ui-fg-muted px-4 py-3",
|
|
779
|
+
children: "Value"
|
|
780
|
+
}
|
|
781
|
+
)
|
|
782
|
+
] }) }),
|
|
783
|
+
/* @__PURE__ */ jsx("tbody", { className: "divide-y divide-ui-border-subtle bg-ui-bg-base", children: descriptors.map((descriptor) => {
|
|
784
|
+
const value = values[descriptor.key];
|
|
785
|
+
const error = errors[descriptor.key];
|
|
786
|
+
return /* @__PURE__ */ jsxs("tr", { children: [
|
|
787
|
+
/* @__PURE__ */ jsx(
|
|
788
|
+
"th",
|
|
789
|
+
{
|
|
790
|
+
scope: "row",
|
|
791
|
+
className: "txt-compact-medium text-ui-fg-base align-top px-4 py-4",
|
|
792
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
793
|
+
/* @__PURE__ */ jsx("span", { children: descriptor.label ?? descriptor.key }),
|
|
794
|
+
/* @__PURE__ */ jsx("span", { className: "txt-compact-xsmall-plus text-ui-fg-muted uppercase tracking-wide", children: descriptor.type })
|
|
795
|
+
] })
|
|
796
|
+
}
|
|
797
|
+
),
|
|
798
|
+
/* @__PURE__ */ jsx("td", { className: "align-top px-4 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
799
|
+
/* @__PURE__ */ jsx(
|
|
800
|
+
ValueField$1,
|
|
801
|
+
{
|
|
802
|
+
descriptor,
|
|
803
|
+
value,
|
|
804
|
+
onStringChange: handleStringChange,
|
|
805
|
+
onBooleanChange: handleBooleanChange
|
|
806
|
+
}
|
|
807
|
+
),
|
|
808
|
+
error && /* @__PURE__ */ jsx(Text, { className: "txt-compact-small text-ui-fg-error", children: error })
|
|
809
|
+
] }) })
|
|
810
|
+
] }, descriptor.key);
|
|
811
|
+
}) })
|
|
812
|
+
] }) }),
|
|
813
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-3 border-t border-ui-border-subtle pt-3 md:flex-row md:items-center md:justify-between", children: [
|
|
814
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted", children: "Changes are stored on the order metadata object. Clearing a field removes the corresponding key on save." }),
|
|
815
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
816
|
+
/* @__PURE__ */ jsx(
|
|
817
|
+
Button,
|
|
818
|
+
{
|
|
819
|
+
variant: "secondary",
|
|
820
|
+
size: "small",
|
|
821
|
+
disabled: !isDirty || isSaving,
|
|
822
|
+
onClick: handleReset,
|
|
823
|
+
children: "Reset"
|
|
824
|
+
}
|
|
825
|
+
),
|
|
826
|
+
/* @__PURE__ */ jsx(
|
|
827
|
+
Button,
|
|
828
|
+
{
|
|
829
|
+
size: "small",
|
|
830
|
+
onClick: handleSubmit,
|
|
831
|
+
disabled: !isDirty || hasErrors || isSaving,
|
|
832
|
+
isLoading: isSaving,
|
|
833
|
+
children: "Save metadata"
|
|
834
|
+
}
|
|
835
|
+
)
|
|
836
|
+
] })
|
|
837
|
+
] })
|
|
838
|
+
] })
|
|
839
|
+
] });
|
|
840
|
+
};
|
|
841
|
+
const ValueField$1 = ({
|
|
842
|
+
descriptor,
|
|
843
|
+
value,
|
|
844
|
+
onStringChange,
|
|
845
|
+
onBooleanChange
|
|
846
|
+
}) => {
|
|
847
|
+
const fileInputRef = useRef(null);
|
|
848
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
849
|
+
const handleFileUpload = async (event) => {
|
|
850
|
+
var _a;
|
|
851
|
+
const file = (_a = event.target.files) == null ? void 0 : _a[0];
|
|
852
|
+
if (!file) {
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
setIsUploading(true);
|
|
856
|
+
try {
|
|
857
|
+
const formData = new FormData();
|
|
858
|
+
formData.append("files", file);
|
|
859
|
+
const response = await fetch("/admin/uploads", {
|
|
860
|
+
method: "POST",
|
|
861
|
+
credentials: "include",
|
|
862
|
+
body: formData
|
|
863
|
+
});
|
|
864
|
+
if (!response.ok) {
|
|
865
|
+
const payload = await response.json().catch(() => null);
|
|
866
|
+
throw new Error((payload == null ? void 0 : payload.message) ?? "File upload failed");
|
|
867
|
+
}
|
|
868
|
+
const result = await response.json();
|
|
869
|
+
if (result.files && result.files.length > 0) {
|
|
870
|
+
const uploadedFile = result.files[0];
|
|
871
|
+
const fileUrl = uploadedFile.url || uploadedFile.key;
|
|
872
|
+
if (fileUrl) {
|
|
873
|
+
onStringChange(descriptor.key, fileUrl);
|
|
874
|
+
toast.success("File uploaded successfully");
|
|
875
|
+
} else {
|
|
876
|
+
throw new Error("File upload succeeded but no URL returned");
|
|
877
|
+
}
|
|
878
|
+
} else {
|
|
879
|
+
throw new Error("File upload failed - no files returned");
|
|
880
|
+
}
|
|
881
|
+
} catch (error) {
|
|
882
|
+
toast.error(
|
|
883
|
+
error instanceof Error ? error.message : "Failed to upload file"
|
|
884
|
+
);
|
|
885
|
+
} finally {
|
|
886
|
+
setIsUploading(false);
|
|
887
|
+
if (fileInputRef.current) {
|
|
888
|
+
fileInputRef.current.value = "";
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
};
|
|
892
|
+
if (descriptor.type === "bool") {
|
|
893
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
894
|
+
/* @__PURE__ */ jsx(
|
|
895
|
+
Switch,
|
|
896
|
+
{
|
|
897
|
+
checked: Boolean(value),
|
|
898
|
+
onCheckedChange: (checked) => onBooleanChange(descriptor.key, Boolean(checked)),
|
|
899
|
+
"aria-label": `Toggle ${descriptor.label ?? descriptor.key}`
|
|
900
|
+
}
|
|
901
|
+
),
|
|
902
|
+
/* @__PURE__ */ jsx(Text, { className: "txt-compact-small text-ui-fg-muted", children: Boolean(value) ? "True" : "False" })
|
|
903
|
+
] });
|
|
904
|
+
}
|
|
905
|
+
if (descriptor.type === "text") {
|
|
906
|
+
return /* @__PURE__ */ jsx(
|
|
907
|
+
Textarea,
|
|
908
|
+
{
|
|
909
|
+
value: value ?? "",
|
|
910
|
+
placeholder: "Enter text",
|
|
911
|
+
rows: 3,
|
|
912
|
+
onChange: (event) => onStringChange(descriptor.key, event.target.value)
|
|
913
|
+
}
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
if (descriptor.type === "number") {
|
|
917
|
+
return /* @__PURE__ */ jsx(
|
|
918
|
+
Input,
|
|
919
|
+
{
|
|
920
|
+
type: "text",
|
|
921
|
+
inputMode: "decimal",
|
|
922
|
+
placeholder: "0.00",
|
|
923
|
+
value: value ?? "",
|
|
924
|
+
onChange: (event) => onStringChange(descriptor.key, event.target.value)
|
|
925
|
+
}
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
929
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
930
|
+
/* @__PURE__ */ jsx(
|
|
931
|
+
Input,
|
|
932
|
+
{
|
|
933
|
+
type: "url",
|
|
934
|
+
placeholder: "https://example.com/file",
|
|
935
|
+
value: value ?? "",
|
|
936
|
+
onChange: (event) => onStringChange(descriptor.key, event.target.value),
|
|
937
|
+
className: "flex-1"
|
|
938
|
+
}
|
|
939
|
+
),
|
|
940
|
+
/* @__PURE__ */ jsx(
|
|
941
|
+
"input",
|
|
942
|
+
{
|
|
943
|
+
ref: fileInputRef,
|
|
944
|
+
type: "file",
|
|
945
|
+
className: "hidden",
|
|
946
|
+
onChange: handleFileUpload,
|
|
947
|
+
disabled: isUploading,
|
|
948
|
+
"aria-label": `Upload file for ${descriptor.label ?? descriptor.key}`
|
|
949
|
+
}
|
|
950
|
+
),
|
|
951
|
+
/* @__PURE__ */ jsx(
|
|
952
|
+
Button,
|
|
953
|
+
{
|
|
954
|
+
type: "button",
|
|
955
|
+
variant: "secondary",
|
|
956
|
+
size: "small",
|
|
957
|
+
onClick: () => {
|
|
958
|
+
var _a;
|
|
959
|
+
return (_a = fileInputRef.current) == null ? void 0 : _a.click();
|
|
960
|
+
},
|
|
961
|
+
disabled: isUploading,
|
|
962
|
+
isLoading: isUploading,
|
|
963
|
+
children: isUploading ? "Uploading..." : "Upload"
|
|
964
|
+
}
|
|
965
|
+
)
|
|
966
|
+
] }),
|
|
967
|
+
typeof value === "string" && value && /* @__PURE__ */ jsx(
|
|
968
|
+
"a",
|
|
969
|
+
{
|
|
970
|
+
className: "txt-compact-small-plus text-ui-fg-interactive underline",
|
|
971
|
+
href: value,
|
|
972
|
+
target: "_blank",
|
|
973
|
+
rel: "noreferrer",
|
|
974
|
+
children: "View file"
|
|
975
|
+
}
|
|
976
|
+
)
|
|
977
|
+
] });
|
|
978
|
+
};
|
|
979
|
+
defineWidgetConfig({
|
|
980
|
+
zone: "order.details.after"
|
|
981
|
+
});
|
|
595
982
|
const CONFIG_DOCS_URL = "https://docs.medusajs.com/admin/extension-points/widgets#product-details";
|
|
596
983
|
const ProductMetadataTableWidget = ({ data }) => {
|
|
597
984
|
const { data: descriptors = [], isPending, isError } = useProductMetadataConfig();
|
|
598
|
-
const
|
|
599
|
-
const [baselineMetadata, setBaselineMetadata] = useState(
|
|
985
|
+
const productId = (data == null ? void 0 : data.id) ?? void 0;
|
|
986
|
+
const [baselineMetadata, setBaselineMetadata] = useState(
|
|
987
|
+
(data == null ? void 0 : data.metadata) ?? {}
|
|
988
|
+
);
|
|
600
989
|
const queryClient = useQueryClient();
|
|
990
|
+
const previousProductIdRef = useRef(productId);
|
|
991
|
+
const isInitializedRef = useRef(false);
|
|
992
|
+
const dataRef = useRef(data);
|
|
993
|
+
const descriptorsRef = useRef(descriptors);
|
|
601
994
|
useEffect(() => {
|
|
602
|
-
|
|
603
|
-
|
|
995
|
+
dataRef.current = data;
|
|
996
|
+
descriptorsRef.current = descriptors;
|
|
997
|
+
}, [data, descriptors]);
|
|
998
|
+
useEffect(() => {
|
|
999
|
+
var _a;
|
|
1000
|
+
if (previousProductIdRef.current === productId && isInitializedRef.current) {
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
const productIdChanged = previousProductIdRef.current !== productId;
|
|
1004
|
+
if (productIdChanged || !isInitializedRef.current) {
|
|
1005
|
+
const currentMetadata = (data == null ? void 0 : data.metadata) ?? ((_a = dataRef.current) == null ? void 0 : _a.metadata) ?? {};
|
|
1006
|
+
const currentDescriptors = descriptorsRef.current.length > 0 ? descriptorsRef.current : descriptors;
|
|
1007
|
+
if (currentDescriptors.length === 0) {
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
previousProductIdRef.current = productId;
|
|
1011
|
+
setBaselineMetadata(currentMetadata);
|
|
1012
|
+
const newInitialState = buildInitialFormState(currentDescriptors, currentMetadata);
|
|
1013
|
+
setValues(newInitialState);
|
|
1014
|
+
isInitializedRef.current = true;
|
|
1015
|
+
}
|
|
1016
|
+
}, [productId]);
|
|
1017
|
+
const metadataStringRef = useRef("");
|
|
1018
|
+
useEffect(() => {
|
|
1019
|
+
const hasMetadata = (data == null ? void 0 : data.metadata) && Object.keys(data.metadata).length > 0;
|
|
1020
|
+
const descriptorsLoaded = descriptors.length > 0;
|
|
1021
|
+
const sameProduct = previousProductIdRef.current === productId;
|
|
1022
|
+
const notInitialized = !isInitializedRef.current;
|
|
1023
|
+
const currentMetadataString = hasMetadata ? JSON.stringify(data.metadata) : "";
|
|
1024
|
+
const metadataChanged = currentMetadataString !== metadataStringRef.current;
|
|
1025
|
+
if (hasMetadata && descriptorsLoaded && sameProduct && (notInitialized || metadataChanged)) {
|
|
1026
|
+
const currentMetadata = data.metadata ?? {};
|
|
1027
|
+
const newInitialState = buildInitialFormState(descriptors, currentMetadata);
|
|
1028
|
+
setBaselineMetadata(currentMetadata);
|
|
1029
|
+
setValues(newInitialState);
|
|
1030
|
+
metadataStringRef.current = currentMetadataString;
|
|
1031
|
+
isInitializedRef.current = true;
|
|
1032
|
+
}
|
|
1033
|
+
}, [data, descriptors.length, productId]);
|
|
604
1034
|
const initialState = useMemo(
|
|
605
1035
|
() => buildInitialFormState(descriptors, baselineMetadata),
|
|
606
1036
|
[descriptors, baselineMetadata]
|
|
607
1037
|
);
|
|
608
|
-
const [values, setValues] = useState(
|
|
609
|
-
initialState
|
|
610
|
-
);
|
|
1038
|
+
const [values, setValues] = useState({});
|
|
611
1039
|
const [isSaving, setIsSaving] = useState(false);
|
|
612
|
-
useEffect(() => {
|
|
613
|
-
setValues(initialState);
|
|
614
|
-
}, [initialState]);
|
|
615
1040
|
const errors = useMemo(() => {
|
|
616
1041
|
return descriptors.reduce((acc, descriptor) => {
|
|
617
1042
|
const error = validateValueForDescriptor(descriptor, values[descriptor.key]);
|
|
@@ -677,6 +1102,15 @@ const ProductMetadataTableWidget = ({ data }) => {
|
|
|
677
1102
|
await queryClient.invalidateQueries({
|
|
678
1103
|
queryKey: ["products"]
|
|
679
1104
|
});
|
|
1105
|
+
await queryClient.invalidateQueries({
|
|
1106
|
+
queryKey: ["product", data.id]
|
|
1107
|
+
});
|
|
1108
|
+
if (data.id) {
|
|
1109
|
+
queryClient.refetchQueries({
|
|
1110
|
+
queryKey: ["product", data.id]
|
|
1111
|
+
}).catch(() => {
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
680
1114
|
} catch (error) {
|
|
681
1115
|
toast.error(error instanceof Error ? error.message : "Save failed");
|
|
682
1116
|
} finally {
|
|
@@ -691,7 +1125,7 @@ const ProductMetadataTableWidget = ({ data }) => {
|
|
|
691
1125
|
] }),
|
|
692
1126
|
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Structured metadata mapped to the keys you configured in the plugin options." })
|
|
693
1127
|
] }),
|
|
694
|
-
isPending ? /* @__PURE__ */ jsx(Skeleton, { className: "h-[160px] w-full" }) : isError ? /* @__PURE__ */ jsxs(InlineTip, { variant: "error", label: "Configuration unavailable", children: [
|
|
1128
|
+
isPending || !isInitializedRef.current || Object.keys(values).length === 0 ? /* @__PURE__ */ jsx(Skeleton, { className: "h-[160px] w-full" }) : isError ? /* @__PURE__ */ jsxs(InlineTip, { variant: "error", label: "Configuration unavailable", children: [
|
|
695
1129
|
"Unable to load metadata configuration for this plugin. Confirm that the plugin is registered with options in ",
|
|
696
1130
|
/* @__PURE__ */ jsx("code", { children: "medusa-config.ts" }),
|
|
697
1131
|
"."
|
|
@@ -1066,6 +1500,10 @@ const widgetModule = { widgets: [
|
|
|
1066
1500
|
Component: HideDefaultMetadataWidget,
|
|
1067
1501
|
zone: ["product.details.side.before"]
|
|
1068
1502
|
},
|
|
1503
|
+
{
|
|
1504
|
+
Component: OrderMetadataTableWidget,
|
|
1505
|
+
zone: ["order.details.after"]
|
|
1506
|
+
},
|
|
1069
1507
|
{
|
|
1070
1508
|
Component: ProductMetadataTableWidget,
|
|
1071
1509
|
zone: ["product.details.after"]
|
|
@@ -6,6 +6,7 @@ const product_helper_options_1 = require("../../../config/product-helper-options
|
|
|
6
6
|
const ENTITY_PARAM = {
|
|
7
7
|
PRODUCT: "product",
|
|
8
8
|
CATEGORY: "category",
|
|
9
|
+
ORDER: "order",
|
|
9
10
|
};
|
|
10
11
|
async function GET(req, res) {
|
|
11
12
|
const configModule = req.scope.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE);
|
|
@@ -13,9 +14,11 @@ async function GET(req, res) {
|
|
|
13
14
|
const entity = req.query?.entity ?? ENTITY_PARAM.PRODUCT;
|
|
14
15
|
const metadataDescriptors = entity === ENTITY_PARAM.CATEGORY
|
|
15
16
|
? options.metadata.categories.descriptors
|
|
16
|
-
:
|
|
17
|
+
: entity === ENTITY_PARAM.ORDER
|
|
18
|
+
? options.metadata.orders.descriptors
|
|
19
|
+
: options.metadata.products.descriptors;
|
|
17
20
|
res.json({
|
|
18
21
|
metadataDescriptors,
|
|
19
22
|
});
|
|
20
23
|
}
|
|
21
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
24
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3Byb2R1Y3QtbWV0YWRhdGEtY29uZmlnL3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBYUEsa0JBa0JDO0FBOUJELHFEQUFxRTtBQUVyRSxtRkFBb0Y7QUFFcEYsTUFBTSxZQUFZLEdBQUc7SUFDbkIsT0FBTyxFQUFFLFNBQVM7SUFDbEIsUUFBUSxFQUFFLFVBQVU7SUFDcEIsS0FBSyxFQUFFLE9BQU87Q0FDTixDQUFBO0FBSUgsS0FBSyxVQUFVLEdBQUcsQ0FBQyxHQUFrQixFQUFFLEdBQW1CO0lBQy9ELE1BQU0sWUFBWSxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUNwQyxpQ0FBeUIsQ0FBQyxhQUFhLENBQ3hDLENBQUE7SUFFRCxNQUFNLE9BQU8sR0FBRyxJQUFBLG9EQUEyQixFQUFDLFlBQVksQ0FBQyxDQUFBO0lBQ3pELE1BQU0sTUFBTSxHQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsTUFBeUIsSUFBSSxZQUFZLENBQUMsT0FBTyxDQUFBO0lBRTVFLE1BQU0sbUJBQW1CLEdBQ3ZCLE1BQU0sS0FBSyxZQUFZLENBQUMsUUFBUTtRQUM5QixDQUFDLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsV0FBVztRQUN6QyxDQUFDLENBQUMsTUFBTSxLQUFLLFlBQVksQ0FBQyxLQUFLO1lBQy9CLENBQUMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxXQUFXO1lBQ3JDLENBQUMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxXQUFXLENBQUE7SUFFM0MsR0FBRyxDQUFDLElBQUksQ0FBQztRQUNQLG1CQUFtQjtLQUNwQixDQUFDLENBQUE7QUFDSixDQUFDIn0=
|
|
@@ -63,6 +63,9 @@ const MetadataSchema = zod_1.z.object({
|
|
|
63
63
|
categories: MetadataCollectionSchema.default({
|
|
64
64
|
descriptors: [],
|
|
65
65
|
}),
|
|
66
|
+
orders: MetadataCollectionSchema.default({
|
|
67
|
+
descriptors: [],
|
|
68
|
+
}),
|
|
66
69
|
});
|
|
67
70
|
const PromotionWindowSchema = zod_1.z.object({
|
|
68
71
|
start_metadata_key: zod_1.z.string().min(1).default("promotion_start"),
|
|
@@ -98,6 +101,9 @@ const ProductHelperOptionsSchema = zod_1.z.object({
|
|
|
98
101
|
categories: {
|
|
99
102
|
descriptors: [],
|
|
100
103
|
},
|
|
104
|
+
orders: {
|
|
105
|
+
descriptors: [],
|
|
106
|
+
},
|
|
101
107
|
}),
|
|
102
108
|
default_price_range: PriceRangeSchema.default({
|
|
103
109
|
label: "custom",
|
|
@@ -143,6 +149,10 @@ function normalizeProductHelperOptions(input) {
|
|
|
143
149
|
...parsed.metadata.categories,
|
|
144
150
|
descriptors: (0, utils_2.normalizeMetadataDescriptors)(parsed.metadata.categories.descriptors),
|
|
145
151
|
},
|
|
152
|
+
orders: {
|
|
153
|
+
...parsed.metadata.orders,
|
|
154
|
+
descriptors: (0, utils_2.normalizeMetadataDescriptors)(parsed.metadata.orders.descriptors),
|
|
155
|
+
},
|
|
146
156
|
},
|
|
147
157
|
filterProviders: parsed.filterProviders ?? [],
|
|
148
158
|
disableBuiltInProviders: parsed.disableBuiltInProviders ?? [],
|
|
@@ -163,4 +173,4 @@ function resolveProductHelperOptions(configModule) {
|
|
|
163
173
|
}
|
|
164
174
|
return exports.DEFAULT_PRODUCT_HELPER_OPTIONS;
|
|
165
175
|
}
|
|
166
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
176
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvZHVjdC1oZWxwZXItb3B0aW9ucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jb25maWcvcHJvZHVjdC1oZWxwZXItb3B0aW9ucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUErTEEsc0VBOEJDO0FBRUQsa0VBbUJDO0FBbFBELHFEQUFvRDtBQUNwRCw2QkFBdUI7QUFFdkIsNERBR3lDO0FBQ3pDLDREQUErRTtBQUUvRSxNQUFNLGdCQUFnQixHQUFHLE9BQUM7S0FDdkIsTUFBTSxDQUFDO0lBQ04sS0FBSyxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUM7SUFDbEQsYUFBYSxFQUFFLE9BQUM7U0FDYixNQUFNLEVBQUU7U0FDUixJQUFJLEVBQUU7U0FDTixNQUFNLENBQUMsQ0FBQyxFQUFFLG1DQUFtQyxDQUFDO1NBQzlDLFNBQVMsQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsS0FBSyxDQUFDLFdBQVcsRUFBRSxDQUFDO1NBQ3pDLFFBQVEsRUFBRTtJQUNiLEdBQUcsRUFBRSxPQUFDO1NBQ0gsTUFBTSxDQUFDO1FBQ04sTUFBTSxFQUFFLElBQUk7UUFDWixrQkFBa0IsRUFBRSw0QkFBNEI7S0FDakQsQ0FBQztTQUNELFdBQVcsRUFBRTtTQUNiLFFBQVEsRUFBRTtTQUNWLE9BQU8sQ0FBQyxJQUFJLENBQUM7SUFDaEIsR0FBRyxFQUFFLE9BQUM7U0FDSCxNQUFNLENBQUM7UUFDTixNQUFNLEVBQUUsSUFBSTtRQUNaLGtCQUFrQixFQUFFLDRCQUE0QjtLQUNqRCxDQUFDO1NBQ0QsV0FBVyxFQUFFO1NBQ2IsUUFBUSxFQUFFO1NBQ1YsT0FBTyxDQUFDLElBQUksQ0FBQztDQUNqQixDQUFDO0tBQ0QsTUFBTSxDQUNMLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FDUixLQUFL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
|
|
@@ -121,7 +121,11 @@ function coerceMetadataValue(descriptor, value) {
|
|
|
121
121
|
const num = typeof value === "number" ? value : Number(String(value).trim());
|
|
122
122
|
return isNaN(num) ? undefined : num;
|
|
123
123
|
}
|
|
124
|
-
|
|
124
|
+
// For text and file types, trim and check if empty
|
|
125
|
+
const trimmed = String(value).trim();
|
|
126
|
+
if (!trimmed)
|
|
127
|
+
return undefined;
|
|
128
|
+
return trimmed;
|
|
125
129
|
}
|
|
126
130
|
function normalizeKey(value) {
|
|
127
131
|
return typeof value === "string" ? value.trim() || undefined : undefined;
|
|
@@ -146,4 +150,4 @@ function isDeepEqual(a, b) {
|
|
|
146
150
|
return false;
|
|
147
151
|
return aKeys.every(key => isDeepEqual(a[key], b[key]));
|
|
148
152
|
}
|
|
149
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
153
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvc2hhcmVkL3Byb2R1Y3QtbWV0YWRhdGEvdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFjQSxvRUF5QkM7QUFFRCw0Q0FLQztBQUVELHNEQVVDO0FBRUQsb0RBd0JDO0FBRUQsZ0RBZUM7QUFFRCxnRUF1QkM7QUE5SEQsbUNBS2dCO0FBS2hCLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxHQUFHLENBQW9CLDRCQUFvQixDQUFDLENBQUE7QUFDMUUsTUFBTSxhQUFhLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxNQUFNLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQTtBQUM5RCxNQUFNLGNBQWMsR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDLE9BQU8sRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFBO0FBRWhFLFNBQWdCLDRCQUE0QixDQUFDLEtBQWM7SUFDekQsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDO1FBQUUsT0FBTyxFQUFFLENBQUE7SUFFcEMsTUFBTSxRQUFRLEdBQUcsSUFBSSxHQUFHLEVBQVUsQ0FBQTtJQUNsQyxPQUFPLEtBQUs7U0FDVCxNQUFNLENBQUMsQ0FBQyxJQUFJLEVBQW1DLEVBQUUsQ0FDaEQsSUFBSSxJQUFJLE9BQU8sSUFBSSxLQUFLLFFBQVEsQ0FDakM7U0FDQSxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUU7UUFDVixNQUFNLEdBQUcsR0FBRyxZQUFZLENBQUUsSUFBWSxDQUFDLEdBQUcsQ0FBQyxDQUFBO1FBQzNDLE1BQU0sSUFBSSxHQUFHLGFBQWEsQ0FBRSxJQUFZLENBQUMsSUFBSSxDQUFDLENBQUE7UUFDOUMsTUFBTSxLQUFLLEdBQUcsY0FBYyxDQUFFLElBQVksQ0FBQyxLQUFLLENBQUMs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
|