@cmssy/react 9.10.0 → 10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.cjs +9 -727
- package/dist/client.d.cts +4 -82
- package/dist/client.d.ts +4 -82
- package/dist/client.js +4 -696
- package/dist/index.cjs +15 -222
- package/dist/index.d.cts +8 -38
- package/dist/index.d.ts +8 -38
- package/dist/index.js +4 -105
- package/dist/internal-server.cjs +166 -0
- package/dist/internal-server.d.cts +37 -0
- package/dist/internal-server.d.ts +37 -0
- package/dist/internal-server.js +161 -0
- package/dist/internal.cjs +19 -0
- package/dist/internal.d.cts +18 -0
- package/dist/internal.d.ts +18 -0
- package/dist/internal.js +16 -0
- package/dist/{registry-Bfq4Pq18.d.cts → registry-zeNh3t6y.d.cts} +2 -6
- package/dist/{registry-Bfq4Pq18.d.ts → registry-zeNh3t6y.d.ts} +2 -6
- package/package.json +12 -2
package/dist/client.cjs
CHANGED
|
@@ -3,13 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
var react = require('react');
|
|
5
5
|
var core = require('@cmssy/core');
|
|
6
|
+
var internal = require('@cmssy/core/internal');
|
|
6
7
|
var blockErrorBoundary = require('@cmssy/react/block-error-boundary');
|
|
7
8
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
9
|
|
|
9
10
|
// src/registry.ts
|
|
10
|
-
function defineBlock(def) {
|
|
11
|
-
return def;
|
|
12
|
-
}
|
|
13
11
|
function buildBlockMap(blocks) {
|
|
14
12
|
const map = /* @__PURE__ */ Object.create(null);
|
|
15
13
|
for (const block of blocks) map[block.type] = block.component;
|
|
@@ -111,7 +109,7 @@ function useEditBridge(page, config) {
|
|
|
111
109
|
react.useEffect(() => {
|
|
112
110
|
if (typeof window === "undefined" || window.parent === window) return;
|
|
113
111
|
const { editorOrigin } = config;
|
|
114
|
-
let postTarget =
|
|
112
|
+
let postTarget = internal.resolveInitialTarget(editorOrigin);
|
|
115
113
|
const isWildcard = Array.isArray(editorOrigin) ? editorOrigin.includes("*") : editorOrigin === "*";
|
|
116
114
|
if (isWildcard && typeof console !== "undefined") {
|
|
117
115
|
console.warn(
|
|
@@ -352,7 +350,7 @@ function useDragAgent(config) {
|
|
|
352
350
|
react.useEffect(() => {
|
|
353
351
|
if (typeof window === "undefined" || window.parent === window) return;
|
|
354
352
|
const { editorOrigin } = config;
|
|
355
|
-
let postTarget =
|
|
353
|
+
let postTarget = internal.resolveInitialTarget(editorOrigin);
|
|
356
354
|
const isWildcard = Array.isArray(editorOrigin) ? editorOrigin.includes("*") : editorOrigin === "*";
|
|
357
355
|
if (isWildcard && typeof console !== "undefined") {
|
|
358
356
|
console.warn(
|
|
@@ -588,11 +586,11 @@ function CmssyBlock({
|
|
|
588
586
|
)
|
|
589
587
|
);
|
|
590
588
|
}
|
|
591
|
-
const base = resolvedContent ? { ...resolvedContent } :
|
|
589
|
+
const base = resolvedContent ? { ...resolvedContent } : internal.getBlockContentForLanguage(block.content, locale, defaultLocale);
|
|
592
590
|
const content = patchedContent ? { ...base, ...patchedContent } : base;
|
|
593
|
-
if (schema)
|
|
594
|
-
const style = patchedStyle ? { ...
|
|
595
|
-
const advanced = patchedAdvanced ? { ...
|
|
591
|
+
if (schema) internal.normalizeRelationContent(content, schema, resolvedContent);
|
|
592
|
+
const style = patchedStyle ? { ...internal.asBucket(block.style), ...patchedStyle } : internal.asBucket(block.style);
|
|
593
|
+
const advanced = patchedAdvanced ? { ...internal.asBucket(block.advanced), ...patchedAdvanced } : internal.asBucket(block.advanced);
|
|
596
594
|
return wrap(
|
|
597
595
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
598
596
|
blockErrorBoundary.BlockErrorBoundary,
|
|
@@ -659,7 +657,7 @@ function EditableBlocks({
|
|
|
659
657
|
}) {
|
|
660
658
|
const blockMap = react.useMemo(() => buildBlockMap(blocks), [blocks]);
|
|
661
659
|
const context = react.useMemo(
|
|
662
|
-
() =>
|
|
660
|
+
() => internal.buildBlockContext(locale, defaultLocale, enabledLocales, true, forms),
|
|
663
661
|
[locale, defaultLocale, enabledLocales, forms]
|
|
664
662
|
);
|
|
665
663
|
const bridgeConfig = react.useMemo(
|
|
@@ -816,7 +814,7 @@ function CmssyEditableLayout({
|
|
|
816
814
|
}, [groups, position]);
|
|
817
815
|
const patches = useLayoutPatchBridge(position, edit);
|
|
818
816
|
const context = react.useMemo(
|
|
819
|
-
() =>
|
|
817
|
+
() => internal.buildBlockContext(locale, defaultLocale, enabledLocales, true),
|
|
820
818
|
[locale, defaultLocale, enabledLocales]
|
|
821
819
|
);
|
|
822
820
|
if (layoutBlocks.length === 0) return null;
|
|
@@ -866,725 +864,9 @@ function CmssyLazyLayout({ load, ...props }) {
|
|
|
866
864
|
if (!blocks) return null;
|
|
867
865
|
return /* @__PURE__ */ jsxRuntime.jsx(CmssyEditableLayout, { ...props, blocks });
|
|
868
866
|
}
|
|
869
|
-
var LocaleContext = react.createContext(null);
|
|
870
|
-
function CmssyLocaleProvider({
|
|
871
|
-
value,
|
|
872
|
-
children
|
|
873
|
-
}) {
|
|
874
|
-
return /* @__PURE__ */ jsxRuntime.jsx(LocaleContext.Provider, { value, children });
|
|
875
|
-
}
|
|
876
|
-
function useCmssyLocale() {
|
|
877
|
-
return react.useContext(LocaleContext);
|
|
878
|
-
}
|
|
879
|
-
var CmssyAuthContext = react.createContext(null);
|
|
880
|
-
function CmssyAuthProvider({
|
|
881
|
-
children,
|
|
882
|
-
basePath = "/api/cmssy/auth",
|
|
883
|
-
initialUser
|
|
884
|
-
}) {
|
|
885
|
-
const seeded = initialUser !== void 0;
|
|
886
|
-
const [user, setUser] = react.useState(initialUser ?? null);
|
|
887
|
-
const [loading, setLoading] = react.useState(!seeded);
|
|
888
|
-
const generation = react.useRef(0);
|
|
889
|
-
const base = react.useMemo(() => basePath.replace(/\/+$/, ""), [basePath]);
|
|
890
|
-
const commitUser = react.useCallback((next) => {
|
|
891
|
-
generation.current += 1;
|
|
892
|
-
setUser(next);
|
|
893
|
-
}, []);
|
|
894
|
-
const fetchUser = react.useCallback(async () => {
|
|
895
|
-
try {
|
|
896
|
-
const res = await fetch(`${base}/me`, {
|
|
897
|
-
credentials: "same-origin",
|
|
898
|
-
cache: "no-store"
|
|
899
|
-
});
|
|
900
|
-
const data = await res.json();
|
|
901
|
-
return data.user ?? null;
|
|
902
|
-
} catch {
|
|
903
|
-
return null;
|
|
904
|
-
}
|
|
905
|
-
}, [base]);
|
|
906
|
-
react.useEffect(() => {
|
|
907
|
-
if (seeded) return;
|
|
908
|
-
const gen = generation.current;
|
|
909
|
-
void (async () => {
|
|
910
|
-
const next = await fetchUser();
|
|
911
|
-
if (gen === generation.current) {
|
|
912
|
-
setUser(next);
|
|
913
|
-
setLoading(false);
|
|
914
|
-
}
|
|
915
|
-
})();
|
|
916
|
-
}, [fetchUser, seeded]);
|
|
917
|
-
const postAction = react.useCallback(
|
|
918
|
-
async (action, body) => {
|
|
919
|
-
const res = await fetch(`${base}/${action}`, {
|
|
920
|
-
method: "POST",
|
|
921
|
-
credentials: "same-origin",
|
|
922
|
-
headers: { "content-type": "application/json" },
|
|
923
|
-
body: JSON.stringify(body)
|
|
924
|
-
});
|
|
925
|
-
return await res.json();
|
|
926
|
-
},
|
|
927
|
-
[base]
|
|
928
|
-
);
|
|
929
|
-
const signIn = react.useCallback(
|
|
930
|
-
async (identity, password) => {
|
|
931
|
-
const data = await postAction("sign-in", { identity, password });
|
|
932
|
-
if (data.ok && data.user) commitUser(data.user);
|
|
933
|
-
return { ok: Boolean(data.ok), message: data.message };
|
|
934
|
-
},
|
|
935
|
-
[postAction, commitUser]
|
|
936
|
-
);
|
|
937
|
-
const register = react.useCallback(
|
|
938
|
-
async (identity, password, fields3) => {
|
|
939
|
-
const data = await postAction("register", {
|
|
940
|
-
identity,
|
|
941
|
-
password,
|
|
942
|
-
fields: fields3 ?? {}
|
|
943
|
-
});
|
|
944
|
-
return { ok: Boolean(data.ok), message: data.message };
|
|
945
|
-
},
|
|
946
|
-
[postAction]
|
|
947
|
-
);
|
|
948
|
-
const signOut = react.useCallback(async () => {
|
|
949
|
-
try {
|
|
950
|
-
await postAction("sign-out", {});
|
|
951
|
-
} finally {
|
|
952
|
-
commitUser(null);
|
|
953
|
-
}
|
|
954
|
-
}, [postAction, commitUser]);
|
|
955
|
-
const refresh = react.useCallback(async () => {
|
|
956
|
-
const gen = generation.current;
|
|
957
|
-
const next = await fetchUser();
|
|
958
|
-
if (gen === generation.current) setUser(next);
|
|
959
|
-
}, [fetchUser]);
|
|
960
|
-
const value = react.useMemo(
|
|
961
|
-
() => ({ user, loading, signIn, register, signOut, refresh }),
|
|
962
|
-
[user, loading, signIn, register, signOut, refresh]
|
|
963
|
-
);
|
|
964
|
-
return /* @__PURE__ */ jsxRuntime.jsx(CmssyAuthContext.Provider, { value, children });
|
|
965
|
-
}
|
|
966
|
-
function useCmssyUser() {
|
|
967
|
-
const ctx = react.useContext(CmssyAuthContext);
|
|
968
|
-
if (!ctx) {
|
|
969
|
-
throw new Error("useCmssyUser must be used within <CmssyAuthProvider>");
|
|
970
|
-
}
|
|
971
|
-
return ctx;
|
|
972
|
-
}
|
|
973
|
-
var CmssyCommerceContext = react.createContext(null);
|
|
974
|
-
function CmssyCommerceProvider({
|
|
975
|
-
children,
|
|
976
|
-
basePath = "/api/cmssy/cart"
|
|
977
|
-
}) {
|
|
978
|
-
const base = react.useMemo(() => basePath.replace(/\/+$/, ""), [basePath]);
|
|
979
|
-
const generation = react.useRef(0);
|
|
980
|
-
const [cart, setCart] = react.useState(null);
|
|
981
|
-
const [loading, setLoading] = react.useState(true);
|
|
982
|
-
const [error, setError] = react.useState(null);
|
|
983
|
-
const post = react.useCallback(
|
|
984
|
-
async (action, body) => {
|
|
985
|
-
const res = await fetch(`${base}/${action}`, {
|
|
986
|
-
method: "POST",
|
|
987
|
-
credentials: "same-origin",
|
|
988
|
-
headers: { "content-type": "application/json" },
|
|
989
|
-
body: JSON.stringify(body)
|
|
990
|
-
});
|
|
991
|
-
const data = await res.json().catch(() => ({}));
|
|
992
|
-
if (!res.ok) {
|
|
993
|
-
throw new Error(
|
|
994
|
-
typeof data.message === "string" ? data.message : "Commerce request failed"
|
|
995
|
-
);
|
|
996
|
-
}
|
|
997
|
-
return data;
|
|
998
|
-
},
|
|
999
|
-
[base]
|
|
1000
|
-
);
|
|
1001
|
-
const runCart = react.useCallback(
|
|
1002
|
-
async (action, body) => {
|
|
1003
|
-
const gen = ++generation.current;
|
|
1004
|
-
setError(null);
|
|
1005
|
-
try {
|
|
1006
|
-
const data = await post(action, body);
|
|
1007
|
-
if (gen === generation.current) setCart(data.cart);
|
|
1008
|
-
} catch (err) {
|
|
1009
|
-
if (gen === generation.current) {
|
|
1010
|
-
setError(
|
|
1011
|
-
err instanceof Error ? err.message : "Commerce request failed"
|
|
1012
|
-
);
|
|
1013
|
-
}
|
|
1014
|
-
throw err;
|
|
1015
|
-
}
|
|
1016
|
-
},
|
|
1017
|
-
[post]
|
|
1018
|
-
);
|
|
1019
|
-
const refresh = react.useCallback(async () => {
|
|
1020
|
-
setLoading(true);
|
|
1021
|
-
try {
|
|
1022
|
-
await runCart("cart", {});
|
|
1023
|
-
} catch {
|
|
1024
|
-
return;
|
|
1025
|
-
} finally {
|
|
1026
|
-
setLoading(false);
|
|
1027
|
-
}
|
|
1028
|
-
}, [runCart]);
|
|
1029
|
-
react.useEffect(() => {
|
|
1030
|
-
void refresh();
|
|
1031
|
-
}, [refresh]);
|
|
1032
|
-
const addToCart = react.useCallback(
|
|
1033
|
-
(recordId, quantity = 1, options) => runCart("add", {
|
|
1034
|
-
recordId,
|
|
1035
|
-
quantity,
|
|
1036
|
-
variantSelections: options?.variantSelections,
|
|
1037
|
-
notes: options?.notes
|
|
1038
|
-
}),
|
|
1039
|
-
[runCart]
|
|
1040
|
-
);
|
|
1041
|
-
const updateItem = react.useCallback(
|
|
1042
|
-
(itemId, quantity) => runCart("update", { itemId, quantity }),
|
|
1043
|
-
[runCart]
|
|
1044
|
-
);
|
|
1045
|
-
const removeItem = react.useCallback(
|
|
1046
|
-
(itemId) => runCart("remove", { itemId }),
|
|
1047
|
-
[runCart]
|
|
1048
|
-
);
|
|
1049
|
-
const clearCart = react.useCallback(() => runCart("clear", {}), [runCart]);
|
|
1050
|
-
const applyDiscount = react.useCallback(
|
|
1051
|
-
(code) => runCart("apply-discount", { code }),
|
|
1052
|
-
[runCart]
|
|
1053
|
-
);
|
|
1054
|
-
const removeDiscount = react.useCallback(
|
|
1055
|
-
() => runCart("remove-discount", {}),
|
|
1056
|
-
[runCart]
|
|
1057
|
-
);
|
|
1058
|
-
const setShippingMethod = react.useCallback(
|
|
1059
|
-
(shippingMethodId) => runCart("set-shipping", { shippingMethodId }),
|
|
1060
|
-
[runCart]
|
|
1061
|
-
);
|
|
1062
|
-
const merge = react.useCallback(() => runCart("merge", {}), [runCart]);
|
|
1063
|
-
const checkout = react.useCallback(
|
|
1064
|
-
async (input) => {
|
|
1065
|
-
const gen = ++generation.current;
|
|
1066
|
-
setError(null);
|
|
1067
|
-
try {
|
|
1068
|
-
const data = await post("checkout", {
|
|
1069
|
-
...input
|
|
1070
|
-
});
|
|
1071
|
-
if (gen === generation.current) setCart(null);
|
|
1072
|
-
return data.order;
|
|
1073
|
-
} catch (err) {
|
|
1074
|
-
if (gen === generation.current) {
|
|
1075
|
-
setError(err instanceof Error ? err.message : "Checkout failed");
|
|
1076
|
-
}
|
|
1077
|
-
throw err;
|
|
1078
|
-
}
|
|
1079
|
-
},
|
|
1080
|
-
[post]
|
|
1081
|
-
);
|
|
1082
|
-
const fetchProduct = react.useCallback(
|
|
1083
|
-
async (modelSlug, filter) => {
|
|
1084
|
-
const data = await post("product", {
|
|
1085
|
-
modelSlug,
|
|
1086
|
-
filter
|
|
1087
|
-
});
|
|
1088
|
-
return data.product;
|
|
1089
|
-
},
|
|
1090
|
-
[post]
|
|
1091
|
-
);
|
|
1092
|
-
const value = react.useMemo(
|
|
1093
|
-
() => ({
|
|
1094
|
-
cart,
|
|
1095
|
-
loading,
|
|
1096
|
-
error,
|
|
1097
|
-
addToCart,
|
|
1098
|
-
updateItem,
|
|
1099
|
-
removeItem,
|
|
1100
|
-
clearCart,
|
|
1101
|
-
applyDiscount,
|
|
1102
|
-
removeDiscount,
|
|
1103
|
-
setShippingMethod,
|
|
1104
|
-
merge,
|
|
1105
|
-
checkout,
|
|
1106
|
-
refresh,
|
|
1107
|
-
fetchProduct
|
|
1108
|
-
}),
|
|
1109
|
-
[
|
|
1110
|
-
cart,
|
|
1111
|
-
loading,
|
|
1112
|
-
error,
|
|
1113
|
-
addToCart,
|
|
1114
|
-
updateItem,
|
|
1115
|
-
removeItem,
|
|
1116
|
-
clearCart,
|
|
1117
|
-
applyDiscount,
|
|
1118
|
-
removeDiscount,
|
|
1119
|
-
setShippingMethod,
|
|
1120
|
-
merge,
|
|
1121
|
-
checkout,
|
|
1122
|
-
refresh,
|
|
1123
|
-
fetchProduct
|
|
1124
|
-
]
|
|
1125
|
-
);
|
|
1126
|
-
return /* @__PURE__ */ jsxRuntime.jsx(CmssyCommerceContext.Provider, { value, children });
|
|
1127
|
-
}
|
|
1128
|
-
function useCart() {
|
|
1129
|
-
const ctx = react.useContext(CmssyCommerceContext);
|
|
1130
|
-
if (!ctx) {
|
|
1131
|
-
throw new Error("useCart must be used within <CmssyCommerceProvider>");
|
|
1132
|
-
}
|
|
1133
|
-
return ctx;
|
|
1134
|
-
}
|
|
1135
|
-
var productProps = {
|
|
1136
|
-
modelSlug: core.fields.text({ label: "Model slug" }),
|
|
1137
|
-
slugField: core.fields.text({ label: "Slug field" }),
|
|
1138
|
-
slug: core.fields.text({ label: "Product slug" }),
|
|
1139
|
-
nameField: core.fields.text({ label: "Name field" }),
|
|
1140
|
-
priceField: core.fields.text({ label: "Price field" }),
|
|
1141
|
-
imageField: core.fields.text({ label: "Image field" })
|
|
1142
|
-
};
|
|
1143
|
-
function deriveAxes(variants) {
|
|
1144
|
-
const axes = /* @__PURE__ */ new Map();
|
|
1145
|
-
for (const variant of variants) {
|
|
1146
|
-
for (const option of variant.selectedOptions) {
|
|
1147
|
-
if (!axes.has(option.name)) axes.set(option.name, /* @__PURE__ */ new Set());
|
|
1148
|
-
axes.get(option.name).add(option.value);
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
return [...axes.entries()].map(([name, values]) => ({
|
|
1152
|
-
name,
|
|
1153
|
-
values: [...values]
|
|
1154
|
-
}));
|
|
1155
|
-
}
|
|
1156
|
-
function matchVariant(variants, selections) {
|
|
1157
|
-
return variants.find(
|
|
1158
|
-
(variant) => variant.selectedOptions.every(
|
|
1159
|
-
(opt) => selections[opt.name] === opt.value
|
|
1160
|
-
)
|
|
1161
|
-
) ?? null;
|
|
1162
|
-
}
|
|
1163
|
-
function ProductComponent({ content: c }) {
|
|
1164
|
-
const { fetchProduct, addToCart } = useCart();
|
|
1165
|
-
const injected = c.product ?? null;
|
|
1166
|
-
const [product, setProduct] = react.useState(injected);
|
|
1167
|
-
const [loading, setLoading] = react.useState(!injected && Boolean(c.slug));
|
|
1168
|
-
const [selections, setSelections] = react.useState({});
|
|
1169
|
-
const [adding, setAdding] = react.useState(false);
|
|
1170
|
-
const [added, setAdded] = react.useState(false);
|
|
1171
|
-
const [addError, setAddError] = react.useState(null);
|
|
1172
|
-
const modelSlug = c.modelSlug ?? "products";
|
|
1173
|
-
const slugField = c.slugField ?? "slug";
|
|
1174
|
-
const slug = c.slug ?? "";
|
|
1175
|
-
react.useEffect(() => {
|
|
1176
|
-
if (injected || !slug) return;
|
|
1177
|
-
let active = true;
|
|
1178
|
-
setLoading(true);
|
|
1179
|
-
void (async () => {
|
|
1180
|
-
try {
|
|
1181
|
-
const result = await fetchProduct(modelSlug, { [slugField]: slug });
|
|
1182
|
-
if (active) setProduct(result);
|
|
1183
|
-
} finally {
|
|
1184
|
-
if (active) setLoading(false);
|
|
1185
|
-
}
|
|
1186
|
-
})();
|
|
1187
|
-
return () => {
|
|
1188
|
-
active = false;
|
|
1189
|
-
};
|
|
1190
|
-
}, [injected, fetchProduct, modelSlug, slugField, slug]);
|
|
1191
|
-
const axes = react.useMemo(
|
|
1192
|
-
() => product ? deriveAxes(product.variants) : [],
|
|
1193
|
-
[product]
|
|
1194
|
-
);
|
|
1195
|
-
const variant = react.useMemo(
|
|
1196
|
-
() => product ? matchVariant(product.variants, selections) : null,
|
|
1197
|
-
[product, selections]
|
|
1198
|
-
);
|
|
1199
|
-
if (loading) {
|
|
1200
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cmssy-product": "loading", children: "Loading\u2026" });
|
|
1201
|
-
}
|
|
1202
|
-
if (!product) {
|
|
1203
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cmssy-product": "not-found", children: "Product not found" });
|
|
1204
|
-
}
|
|
1205
|
-
const data = product.data;
|
|
1206
|
-
const name = String(data[c.nameField ?? "name"] ?? "");
|
|
1207
|
-
const imageUrl = c.imageField ? data[c.imageField] : null;
|
|
1208
|
-
const hasVariants = product.variants.length > 0;
|
|
1209
|
-
const currency = data.currency ?? "USD";
|
|
1210
|
-
const priceMinor = hasVariants ? variant?.price : core.toMinorUnits(Number(data[c.priceField ?? "price"] ?? 0), currency);
|
|
1211
|
-
const showPrice = priceMinor != null && Number.isFinite(priceMinor);
|
|
1212
|
-
const allAxesSelected = axes.every((axis) => selections[axis.name]);
|
|
1213
|
-
const outOfStock = variant != null && variant.inventory != null && variant.inventory <= 0;
|
|
1214
|
-
const canAdd = hasVariants ? Boolean(variant) && !outOfStock : true;
|
|
1215
|
-
async function onAdd() {
|
|
1216
|
-
if (!product) return;
|
|
1217
|
-
setAdding(true);
|
|
1218
|
-
setAdded(false);
|
|
1219
|
-
setAddError(null);
|
|
1220
|
-
try {
|
|
1221
|
-
await addToCart(
|
|
1222
|
-
product.id,
|
|
1223
|
-
1,
|
|
1224
|
-
hasVariants ? { variantSelections: selections } : void 0
|
|
1225
|
-
);
|
|
1226
|
-
setAdded(true);
|
|
1227
|
-
} catch (err) {
|
|
1228
|
-
setAddError(err instanceof Error ? err.message : "Could not add to cart");
|
|
1229
|
-
} finally {
|
|
1230
|
-
setAdding(false);
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cmssy-product": product.id, children: [
|
|
1234
|
-
imageUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: imageUrl, alt: name }) : null,
|
|
1235
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { "data-cmssy-product-name": true, children: name }),
|
|
1236
|
-
showPrice ? /* @__PURE__ */ jsxRuntime.jsx("p", { "data-cmssy-product-price": true, children: core.formatPrice(priceMinor, currency) }) : null,
|
|
1237
|
-
axes.map((axis) => /* @__PURE__ */ jsxRuntime.jsxs("label", { "data-cmssy-variant-axis": axis.name, children: [
|
|
1238
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: axis.name }),
|
|
1239
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1240
|
-
"select",
|
|
1241
|
-
{
|
|
1242
|
-
value: selections[axis.name] ?? "",
|
|
1243
|
-
onChange: (e) => setSelections((prev) => ({
|
|
1244
|
-
...prev,
|
|
1245
|
-
[axis.name]: e.target.value
|
|
1246
|
-
})),
|
|
1247
|
-
children: [
|
|
1248
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u2014" }),
|
|
1249
|
-
axis.values.map((v) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: v, children: v }, v))
|
|
1250
|
-
]
|
|
1251
|
-
}
|
|
1252
|
-
)
|
|
1253
|
-
] }, axis.name)),
|
|
1254
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1255
|
-
"button",
|
|
1256
|
-
{
|
|
1257
|
-
type: "button",
|
|
1258
|
-
onClick: onAdd,
|
|
1259
|
-
disabled: adding || !canAdd || hasVariants && !allAxesSelected,
|
|
1260
|
-
"data-cmssy-add-to-cart": true,
|
|
1261
|
-
children: adding ? "Adding\u2026" : outOfStock ? "Out of stock" : added ? "Added" : "Add to cart"
|
|
1262
|
-
}
|
|
1263
|
-
),
|
|
1264
|
-
addError ? /* @__PURE__ */ jsxRuntime.jsx("p", { "data-cmssy-product-error": true, children: addError }) : null
|
|
1265
|
-
] });
|
|
1266
|
-
}
|
|
1267
|
-
var productBlock = defineBlock({
|
|
1268
|
-
type: "product",
|
|
1269
|
-
label: "Product",
|
|
1270
|
-
category: "Commerce",
|
|
1271
|
-
props: productProps,
|
|
1272
|
-
component: ProductComponent
|
|
1273
|
-
});
|
|
1274
|
-
function CartComponent() {
|
|
1275
|
-
const {
|
|
1276
|
-
cart,
|
|
1277
|
-
loading,
|
|
1278
|
-
error,
|
|
1279
|
-
updateItem,
|
|
1280
|
-
removeItem,
|
|
1281
|
-
applyDiscount,
|
|
1282
|
-
removeDiscount
|
|
1283
|
-
} = useCart();
|
|
1284
|
-
const [code, setCode] = react.useState("");
|
|
1285
|
-
const [busy, setBusy] = react.useState(false);
|
|
1286
|
-
if (loading) {
|
|
1287
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cmssy-cart": "loading", children: "Loading\u2026" });
|
|
1288
|
-
}
|
|
1289
|
-
if (!cart || cart.items.length === 0) {
|
|
1290
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cmssy-cart": "empty", children: "Your cart is empty." });
|
|
1291
|
-
}
|
|
1292
|
-
const currency = cart.currency ?? "USD";
|
|
1293
|
-
async function withBusy(fn) {
|
|
1294
|
-
setBusy(true);
|
|
1295
|
-
try {
|
|
1296
|
-
await fn();
|
|
1297
|
-
} catch {
|
|
1298
|
-
setBusy(false);
|
|
1299
|
-
} finally {
|
|
1300
|
-
setBusy(false);
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cmssy-cart": cart.id, children: [
|
|
1304
|
-
error ? /* @__PURE__ */ jsxRuntime.jsx("p", { "data-cmssy-cart-error": true, children: error }) : null,
|
|
1305
|
-
/* @__PURE__ */ jsxRuntime.jsx("ul", { "data-cmssy-cart-items": true, children: cart.items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("li", { "data-cmssy-cart-item": item.id, children: [
|
|
1306
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-cmssy-item-name": true, children: item.snapshot.name }),
|
|
1307
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1308
|
-
"input",
|
|
1309
|
-
{
|
|
1310
|
-
type: "number",
|
|
1311
|
-
min: "1",
|
|
1312
|
-
value: item.quantity,
|
|
1313
|
-
disabled: busy,
|
|
1314
|
-
onChange: (e) => {
|
|
1315
|
-
const qty = Number(e.target.value);
|
|
1316
|
-
if (Number.isInteger(qty) && qty >= 1) {
|
|
1317
|
-
void withBusy(() => updateItem(item.id, qty));
|
|
1318
|
-
}
|
|
1319
|
-
},
|
|
1320
|
-
"data-cmssy-item-qty": true
|
|
1321
|
-
}
|
|
1322
|
-
),
|
|
1323
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-cmssy-item-price": true, children: core.formatPrice(item.snapshot.price * item.quantity, currency) }),
|
|
1324
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1325
|
-
"button",
|
|
1326
|
-
{
|
|
1327
|
-
type: "button",
|
|
1328
|
-
disabled: busy,
|
|
1329
|
-
onClick: () => void withBusy(() => removeItem(item.id)),
|
|
1330
|
-
"data-cmssy-item-remove": true,
|
|
1331
|
-
children: "Remove"
|
|
1332
|
-
}
|
|
1333
|
-
)
|
|
1334
|
-
] }, item.id)) }),
|
|
1335
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { "data-cmssy-cart-discount": true, children: cart.appliedDiscount ? /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
1336
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: cart.appliedDiscount.code }),
|
|
1337
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1338
|
-
"button",
|
|
1339
|
-
{
|
|
1340
|
-
type: "button",
|
|
1341
|
-
disabled: busy,
|
|
1342
|
-
onClick: () => void withBusy(() => removeDiscount()),
|
|
1343
|
-
children: "Remove"
|
|
1344
|
-
}
|
|
1345
|
-
)
|
|
1346
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
1347
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1348
|
-
"input",
|
|
1349
|
-
{
|
|
1350
|
-
type: "text",
|
|
1351
|
-
value: code,
|
|
1352
|
-
placeholder: "Discount code",
|
|
1353
|
-
onChange: (e) => setCode(e.target.value),
|
|
1354
|
-
disabled: busy
|
|
1355
|
-
}
|
|
1356
|
-
),
|
|
1357
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1358
|
-
"button",
|
|
1359
|
-
{
|
|
1360
|
-
type: "button",
|
|
1361
|
-
disabled: busy || !code.trim(),
|
|
1362
|
-
onClick: () => void withBusy(async () => {
|
|
1363
|
-
await applyDiscount(code.trim());
|
|
1364
|
-
setCode("");
|
|
1365
|
-
}),
|
|
1366
|
-
children: "Apply"
|
|
1367
|
-
}
|
|
1368
|
-
)
|
|
1369
|
-
] }) }),
|
|
1370
|
-
/* @__PURE__ */ jsxRuntime.jsxs("dl", { "data-cmssy-cart-totals": true, children: [
|
|
1371
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1372
|
-
/* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Subtotal" }),
|
|
1373
|
-
/* @__PURE__ */ jsxRuntime.jsx("dd", { children: core.formatPrice(cart.subtotal, currency) })
|
|
1374
|
-
] }),
|
|
1375
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1376
|
-
/* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Total" }),
|
|
1377
|
-
/* @__PURE__ */ jsxRuntime.jsx("dd", { "data-cmssy-cart-total": true, children: core.formatPrice(cart.discountedTotal, currency) })
|
|
1378
|
-
] })
|
|
1379
|
-
] })
|
|
1380
|
-
] });
|
|
1381
|
-
}
|
|
1382
|
-
var cartBlock = defineBlock({
|
|
1383
|
-
type: "cart",
|
|
1384
|
-
label: "Cart",
|
|
1385
|
-
category: "Commerce",
|
|
1386
|
-
props: {},
|
|
1387
|
-
component: CartComponent
|
|
1388
|
-
});
|
|
1389
|
-
var checkoutProps = {
|
|
1390
|
-
successMessage: core.fields.text({ label: "Success message" })
|
|
1391
|
-
};
|
|
1392
|
-
var EMAIL_RE = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
|
|
1393
|
-
function CheckoutComponent({
|
|
1394
|
-
content: c
|
|
1395
|
-
}) {
|
|
1396
|
-
const { cart, loading, checkout } = useCart();
|
|
1397
|
-
const [email, setEmail] = react.useState("");
|
|
1398
|
-
const [busy, setBusy] = react.useState(false);
|
|
1399
|
-
const [error, setError] = react.useState(null);
|
|
1400
|
-
const [order, setOrder] = react.useState(null);
|
|
1401
|
-
if (order) {
|
|
1402
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cmssy-checkout": "done", children: [
|
|
1403
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { children: c.successMessage ?? "Order placed - awaiting payment." }),
|
|
1404
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { "data-cmssy-order-id": true, children: order.id }),
|
|
1405
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { "data-cmssy-order-total": true, children: core.formatPrice(order.total, order.currency) })
|
|
1406
|
-
] });
|
|
1407
|
-
}
|
|
1408
|
-
if (loading) {
|
|
1409
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cmssy-checkout": "loading", children: "Loading\u2026" });
|
|
1410
|
-
}
|
|
1411
|
-
if (!cart || cart.items.length === 0) {
|
|
1412
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cmssy-checkout": "empty", children: "Your cart is empty." });
|
|
1413
|
-
}
|
|
1414
|
-
const emailValid = EMAIL_RE.test(email.trim());
|
|
1415
|
-
async function onSubmit() {
|
|
1416
|
-
setBusy(true);
|
|
1417
|
-
setError(null);
|
|
1418
|
-
try {
|
|
1419
|
-
setOrder(await checkout({ customerEmail: email.trim() }));
|
|
1420
|
-
} catch (err) {
|
|
1421
|
-
setError(err instanceof Error ? err.message : "Checkout failed");
|
|
1422
|
-
} finally {
|
|
1423
|
-
setBusy(false);
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1426
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1427
|
-
"form",
|
|
1428
|
-
{
|
|
1429
|
-
"data-cmssy-checkout": cart.id,
|
|
1430
|
-
onSubmit: (e) => {
|
|
1431
|
-
e.preventDefault();
|
|
1432
|
-
if (emailValid && !busy) void onSubmit();
|
|
1433
|
-
},
|
|
1434
|
-
children: [
|
|
1435
|
-
error ? /* @__PURE__ */ jsxRuntime.jsx("p", { "data-cmssy-checkout-error": true, children: error }) : null,
|
|
1436
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
1437
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Email" }),
|
|
1438
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1439
|
-
"input",
|
|
1440
|
-
{
|
|
1441
|
-
type: "email",
|
|
1442
|
-
value: email,
|
|
1443
|
-
onChange: (e) => setEmail(e.target.value),
|
|
1444
|
-
required: true,
|
|
1445
|
-
"data-cmssy-checkout-email": true
|
|
1446
|
-
}
|
|
1447
|
-
)
|
|
1448
|
-
] }),
|
|
1449
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { "data-cmssy-checkout-total": true, children: core.formatPrice(cart.discountedTotal, cart.currency ?? "USD") }),
|
|
1450
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1451
|
-
"button",
|
|
1452
|
-
{
|
|
1453
|
-
type: "submit",
|
|
1454
|
-
disabled: busy || !emailValid,
|
|
1455
|
-
"data-cmssy-checkout-submit": true,
|
|
1456
|
-
children: busy ? "Placing order\u2026" : "Place order"
|
|
1457
|
-
}
|
|
1458
|
-
)
|
|
1459
|
-
]
|
|
1460
|
-
}
|
|
1461
|
-
);
|
|
1462
|
-
}
|
|
1463
|
-
var checkoutBlock = defineBlock({
|
|
1464
|
-
type: "checkout",
|
|
1465
|
-
label: "Checkout",
|
|
1466
|
-
category: "Commerce",
|
|
1467
|
-
props: checkoutProps,
|
|
1468
|
-
component: CheckoutComponent
|
|
1469
|
-
});
|
|
1470
|
-
function useCmssyOrders(options = {}) {
|
|
1471
|
-
const base = (options.basePath ?? "/api/cmssy/orders").replace(/\/+$/, "");
|
|
1472
|
-
const skip = options.skip ?? 0;
|
|
1473
|
-
const limit = options.limit ?? 20;
|
|
1474
|
-
const [orders, setOrders] = react.useState([]);
|
|
1475
|
-
const [total, setTotal] = react.useState(0);
|
|
1476
|
-
const [hasMore, setHasMore] = react.useState(false);
|
|
1477
|
-
const [loading, setLoading] = react.useState(true);
|
|
1478
|
-
const [error, setError] = react.useState(null);
|
|
1479
|
-
const load = react.useCallback(async () => {
|
|
1480
|
-
setLoading(true);
|
|
1481
|
-
setError(null);
|
|
1482
|
-
try {
|
|
1483
|
-
const qs = new URLSearchParams({
|
|
1484
|
-
skip: String(skip),
|
|
1485
|
-
limit: String(limit)
|
|
1486
|
-
});
|
|
1487
|
-
const res = await fetch(`${base}?${qs.toString()}`, {
|
|
1488
|
-
credentials: "same-origin",
|
|
1489
|
-
cache: "no-store"
|
|
1490
|
-
});
|
|
1491
|
-
if (res.status === 401) {
|
|
1492
|
-
setOrders([]);
|
|
1493
|
-
setTotal(0);
|
|
1494
|
-
setHasMore(false);
|
|
1495
|
-
return;
|
|
1496
|
-
}
|
|
1497
|
-
if (!res.ok) {
|
|
1498
|
-
const body = await res.json().catch(() => ({}));
|
|
1499
|
-
throw new Error(
|
|
1500
|
-
body.message ?? `Orders request failed (${res.status})`
|
|
1501
|
-
);
|
|
1502
|
-
}
|
|
1503
|
-
const data = await res.json();
|
|
1504
|
-
setOrders(data.items ?? []);
|
|
1505
|
-
setTotal(data.total ?? 0);
|
|
1506
|
-
setHasMore(Boolean(data.hasMore));
|
|
1507
|
-
} catch (e) {
|
|
1508
|
-
setError(e instanceof Error ? e.message : "Could not load orders");
|
|
1509
|
-
} finally {
|
|
1510
|
-
setLoading(false);
|
|
1511
|
-
}
|
|
1512
|
-
}, [base, skip, limit]);
|
|
1513
|
-
react.useEffect(() => {
|
|
1514
|
-
void load();
|
|
1515
|
-
}, [load]);
|
|
1516
|
-
return { orders, total, hasMore, loading, error, refresh: load };
|
|
1517
|
-
}
|
|
1518
|
-
function useCmssyOrder(id, options = {}) {
|
|
1519
|
-
const base = (options.basePath ?? "/api/cmssy/orders").replace(/\/+$/, "");
|
|
1520
|
-
const [order, setOrder] = react.useState(null);
|
|
1521
|
-
const [loading, setLoading] = react.useState(Boolean(id));
|
|
1522
|
-
const [error, setError] = react.useState(null);
|
|
1523
|
-
const load = react.useCallback(async () => {
|
|
1524
|
-
if (!id) {
|
|
1525
|
-
setOrder(null);
|
|
1526
|
-
setLoading(false);
|
|
1527
|
-
return;
|
|
1528
|
-
}
|
|
1529
|
-
setLoading(true);
|
|
1530
|
-
setError(null);
|
|
1531
|
-
try {
|
|
1532
|
-
const qs = new URLSearchParams({ id });
|
|
1533
|
-
const res = await fetch(`${base}?${qs.toString()}`, {
|
|
1534
|
-
credentials: "same-origin",
|
|
1535
|
-
cache: "no-store"
|
|
1536
|
-
});
|
|
1537
|
-
if (res.status === 401) {
|
|
1538
|
-
setOrder(null);
|
|
1539
|
-
return;
|
|
1540
|
-
}
|
|
1541
|
-
if (!res.ok) {
|
|
1542
|
-
const body = await res.json().catch(() => ({}));
|
|
1543
|
-
throw new Error(body.message ?? `Order request failed (${res.status})`);
|
|
1544
|
-
}
|
|
1545
|
-
const data = await res.json();
|
|
1546
|
-
setOrder(data.order ?? null);
|
|
1547
|
-
} catch (e) {
|
|
1548
|
-
setError(e instanceof Error ? e.message : "Could not load the order");
|
|
1549
|
-
} finally {
|
|
1550
|
-
setLoading(false);
|
|
1551
|
-
}
|
|
1552
|
-
}, [base, id]);
|
|
1553
|
-
react.useEffect(() => {
|
|
1554
|
-
void load();
|
|
1555
|
-
}, [load]);
|
|
1556
|
-
return { order, loading, error, refresh: load };
|
|
1557
|
-
}
|
|
1558
867
|
|
|
1559
|
-
Object.defineProperty(exports, "formatPrice", {
|
|
1560
|
-
enumerable: true,
|
|
1561
|
-
get: function () { return core.formatPrice; }
|
|
1562
|
-
});
|
|
1563
|
-
Object.defineProperty(exports, "fractionDigits", {
|
|
1564
|
-
enumerable: true,
|
|
1565
|
-
get: function () { return core.fractionDigits; }
|
|
1566
|
-
});
|
|
1567
|
-
Object.defineProperty(exports, "fromMinorUnits", {
|
|
1568
|
-
enumerable: true,
|
|
1569
|
-
get: function () { return core.fromMinorUnits; }
|
|
1570
|
-
});
|
|
1571
|
-
Object.defineProperty(exports, "toMinorUnits", {
|
|
1572
|
-
enumerable: true,
|
|
1573
|
-
get: function () { return core.toMinorUnits; }
|
|
1574
|
-
});
|
|
1575
|
-
exports.CmssyAuthProvider = CmssyAuthProvider;
|
|
1576
|
-
exports.CmssyCommerceProvider = CmssyCommerceProvider;
|
|
1577
868
|
exports.CmssyEditableLayout = CmssyEditableLayout;
|
|
1578
869
|
exports.CmssyEditablePage = CmssyEditablePage;
|
|
1579
870
|
exports.CmssyLazyEditor = CmssyLazyEditor;
|
|
1580
871
|
exports.CmssyLazyLayout = CmssyLazyLayout;
|
|
1581
|
-
exports.CmssyLocaleProvider = CmssyLocaleProvider;
|
|
1582
|
-
exports.cartBlock = cartBlock;
|
|
1583
|
-
exports.checkoutBlock = checkoutBlock;
|
|
1584
|
-
exports.productBlock = productBlock;
|
|
1585
|
-
exports.useCart = useCart;
|
|
1586
|
-
exports.useCmssyLocale = useCmssyLocale;
|
|
1587
|
-
exports.useCmssyOrder = useCmssyOrder;
|
|
1588
|
-
exports.useCmssyOrders = useCmssyOrders;
|
|
1589
|
-
exports.useCmssyUser = useCmssyUser;
|
|
1590
872
|
exports.useEditBridge = useEditBridge;
|