@cmssy/react 0.1.9 → 0.2.1
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 +575 -2
- package/dist/client.d.cts +39 -2
- package/dist/client.d.ts +39 -2
- package/dist/client.js +567 -3
- package/dist/{registry-e3O4s_bN.d.cts → commerce-queries-DV7PZlKS.d.cts} +57 -1
- package/dist/{registry-e3O4s_bN.d.ts → commerce-queries-DV7PZlKS.d.ts} +57 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/package.json +1 -1
package/dist/client.cjs
CHANGED
|
@@ -5,6 +5,9 @@ var react = require('react');
|
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
|
|
7
7
|
// src/registry.ts
|
|
8
|
+
function defineBlock(def) {
|
|
9
|
+
return def;
|
|
10
|
+
}
|
|
8
11
|
function buildBlockMap(blocks) {
|
|
9
12
|
const map = /* @__PURE__ */ Object.create(null);
|
|
10
13
|
for (const block of blocks) map[block.type] = block.component;
|
|
@@ -806,11 +809,11 @@ function CmssyAuthProvider({
|
|
|
806
809
|
[postAction, commitUser]
|
|
807
810
|
);
|
|
808
811
|
const register = react.useCallback(
|
|
809
|
-
async (identity, password,
|
|
812
|
+
async (identity, password, fields2) => {
|
|
810
813
|
const data = await postAction("register", {
|
|
811
814
|
identity,
|
|
812
815
|
password,
|
|
813
|
-
fields:
|
|
816
|
+
fields: fields2 ?? {}
|
|
814
817
|
});
|
|
815
818
|
return { ok: Boolean(data.ok), message: data.message };
|
|
816
819
|
},
|
|
@@ -841,11 +844,581 @@ function useCmssyUser() {
|
|
|
841
844
|
}
|
|
842
845
|
return ctx;
|
|
843
846
|
}
|
|
847
|
+
var CmssyCommerceContext = react.createContext(null);
|
|
848
|
+
function CmssyCommerceProvider({
|
|
849
|
+
children,
|
|
850
|
+
basePath = "/api/cmssy/cart"
|
|
851
|
+
}) {
|
|
852
|
+
const base = react.useMemo(() => basePath.replace(/\/+$/, ""), [basePath]);
|
|
853
|
+
const generation = react.useRef(0);
|
|
854
|
+
const [cart, setCart] = react.useState(null);
|
|
855
|
+
const [loading, setLoading] = react.useState(true);
|
|
856
|
+
const [error, setError] = react.useState(null);
|
|
857
|
+
const post = react.useCallback(
|
|
858
|
+
async (action, body) => {
|
|
859
|
+
const res = await fetch(`${base}/${action}`, {
|
|
860
|
+
method: "POST",
|
|
861
|
+
credentials: "same-origin",
|
|
862
|
+
headers: { "content-type": "application/json" },
|
|
863
|
+
body: JSON.stringify(body)
|
|
864
|
+
});
|
|
865
|
+
const data = await res.json().catch(() => ({}));
|
|
866
|
+
if (!res.ok) {
|
|
867
|
+
throw new Error(
|
|
868
|
+
typeof data.message === "string" ? data.message : "Commerce request failed"
|
|
869
|
+
);
|
|
870
|
+
}
|
|
871
|
+
return data;
|
|
872
|
+
},
|
|
873
|
+
[base]
|
|
874
|
+
);
|
|
875
|
+
const runCart = react.useCallback(
|
|
876
|
+
async (action, body) => {
|
|
877
|
+
const gen = ++generation.current;
|
|
878
|
+
setError(null);
|
|
879
|
+
try {
|
|
880
|
+
const data = await post(action, body);
|
|
881
|
+
if (gen === generation.current) setCart(data.cart);
|
|
882
|
+
} catch (err) {
|
|
883
|
+
if (gen === generation.current) {
|
|
884
|
+
setError(
|
|
885
|
+
err instanceof Error ? err.message : "Commerce request failed"
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
throw err;
|
|
889
|
+
}
|
|
890
|
+
},
|
|
891
|
+
[post]
|
|
892
|
+
);
|
|
893
|
+
const refresh = react.useCallback(async () => {
|
|
894
|
+
setLoading(true);
|
|
895
|
+
try {
|
|
896
|
+
await runCart("cart", {});
|
|
897
|
+
} catch {
|
|
898
|
+
return;
|
|
899
|
+
} finally {
|
|
900
|
+
setLoading(false);
|
|
901
|
+
}
|
|
902
|
+
}, [runCart]);
|
|
903
|
+
react.useEffect(() => {
|
|
904
|
+
void refresh();
|
|
905
|
+
}, [refresh]);
|
|
906
|
+
const addToCart = react.useCallback(
|
|
907
|
+
(recordId, quantity = 1, options) => runCart("add", {
|
|
908
|
+
recordId,
|
|
909
|
+
quantity,
|
|
910
|
+
variantSelections: options?.variantSelections,
|
|
911
|
+
notes: options?.notes
|
|
912
|
+
}),
|
|
913
|
+
[runCart]
|
|
914
|
+
);
|
|
915
|
+
const updateItem = react.useCallback(
|
|
916
|
+
(itemId, quantity) => runCart("update", { itemId, quantity }),
|
|
917
|
+
[runCart]
|
|
918
|
+
);
|
|
919
|
+
const removeItem = react.useCallback(
|
|
920
|
+
(itemId) => runCart("remove", { itemId }),
|
|
921
|
+
[runCart]
|
|
922
|
+
);
|
|
923
|
+
const clearCart = react.useCallback(() => runCart("clear", {}), [runCart]);
|
|
924
|
+
const applyDiscount = react.useCallback(
|
|
925
|
+
(code) => runCart("apply-discount", { code }),
|
|
926
|
+
[runCart]
|
|
927
|
+
);
|
|
928
|
+
const removeDiscount = react.useCallback(
|
|
929
|
+
() => runCart("remove-discount", {}),
|
|
930
|
+
[runCart]
|
|
931
|
+
);
|
|
932
|
+
const checkout = react.useCallback(
|
|
933
|
+
async (customerEmail) => {
|
|
934
|
+
const gen = ++generation.current;
|
|
935
|
+
setError(null);
|
|
936
|
+
try {
|
|
937
|
+
const data = await post("checkout", {
|
|
938
|
+
customerEmail
|
|
939
|
+
});
|
|
940
|
+
if (gen === generation.current) setCart(null);
|
|
941
|
+
return data.order;
|
|
942
|
+
} catch (err) {
|
|
943
|
+
if (gen === generation.current) {
|
|
944
|
+
setError(err instanceof Error ? err.message : "Checkout failed");
|
|
945
|
+
}
|
|
946
|
+
throw err;
|
|
947
|
+
}
|
|
948
|
+
},
|
|
949
|
+
[post]
|
|
950
|
+
);
|
|
951
|
+
const fetchProduct = react.useCallback(
|
|
952
|
+
async (modelSlug, filter) => {
|
|
953
|
+
const data = await post("product", {
|
|
954
|
+
modelSlug,
|
|
955
|
+
filter
|
|
956
|
+
});
|
|
957
|
+
return data.product;
|
|
958
|
+
},
|
|
959
|
+
[post]
|
|
960
|
+
);
|
|
961
|
+
const value = react.useMemo(
|
|
962
|
+
() => ({
|
|
963
|
+
cart,
|
|
964
|
+
loading,
|
|
965
|
+
error,
|
|
966
|
+
addToCart,
|
|
967
|
+
updateItem,
|
|
968
|
+
removeItem,
|
|
969
|
+
clearCart,
|
|
970
|
+
applyDiscount,
|
|
971
|
+
removeDiscount,
|
|
972
|
+
checkout,
|
|
973
|
+
refresh,
|
|
974
|
+
fetchProduct
|
|
975
|
+
}),
|
|
976
|
+
[
|
|
977
|
+
cart,
|
|
978
|
+
loading,
|
|
979
|
+
error,
|
|
980
|
+
addToCart,
|
|
981
|
+
updateItem,
|
|
982
|
+
removeItem,
|
|
983
|
+
clearCart,
|
|
984
|
+
applyDiscount,
|
|
985
|
+
removeDiscount,
|
|
986
|
+
checkout,
|
|
987
|
+
refresh,
|
|
988
|
+
fetchProduct
|
|
989
|
+
]
|
|
990
|
+
);
|
|
991
|
+
return /* @__PURE__ */ jsxRuntime.jsx(CmssyCommerceContext.Provider, { value, children });
|
|
992
|
+
}
|
|
993
|
+
function useCart() {
|
|
994
|
+
const ctx = react.useContext(CmssyCommerceContext);
|
|
995
|
+
if (!ctx) {
|
|
996
|
+
throw new Error("useCart must be used within <CmssyCommerceProvider>");
|
|
997
|
+
}
|
|
998
|
+
return ctx;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
// src/fields.ts
|
|
1002
|
+
function control(type) {
|
|
1003
|
+
return (opts = {}) => ({
|
|
1004
|
+
type,
|
|
1005
|
+
label: opts.label ?? "",
|
|
1006
|
+
...opts
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
var fields = {
|
|
1010
|
+
singleLine: control("singleLine"),
|
|
1011
|
+
multiLine: control("multiLine"),
|
|
1012
|
+
richText: control("richText"),
|
|
1013
|
+
numeric: control("numeric"),
|
|
1014
|
+
date: control("date"),
|
|
1015
|
+
media: control("media"),
|
|
1016
|
+
link: control("link"),
|
|
1017
|
+
select: control("select"),
|
|
1018
|
+
multiselect: control("multiselect"),
|
|
1019
|
+
boolean: control("boolean"),
|
|
1020
|
+
color: control("color"),
|
|
1021
|
+
repeater: control("repeater")
|
|
1022
|
+
};
|
|
1023
|
+
|
|
1024
|
+
// src/commerce/money.ts
|
|
1025
|
+
var ZERO_DECIMAL = /* @__PURE__ */ new Set([
|
|
1026
|
+
"BIF",
|
|
1027
|
+
"CLP",
|
|
1028
|
+
"DJF",
|
|
1029
|
+
"GNF",
|
|
1030
|
+
"JPY",
|
|
1031
|
+
"KMF",
|
|
1032
|
+
"KRW",
|
|
1033
|
+
"MGA",
|
|
1034
|
+
"PYG",
|
|
1035
|
+
"RWF",
|
|
1036
|
+
"UGX",
|
|
1037
|
+
"VND",
|
|
1038
|
+
"VUV",
|
|
1039
|
+
"XAF",
|
|
1040
|
+
"XOF",
|
|
1041
|
+
"XPF"
|
|
1042
|
+
]);
|
|
1043
|
+
var THREE_DECIMAL = /* @__PURE__ */ new Set([
|
|
1044
|
+
"BHD",
|
|
1045
|
+
"IQD",
|
|
1046
|
+
"JOD",
|
|
1047
|
+
"KWD",
|
|
1048
|
+
"LYD",
|
|
1049
|
+
"OMR",
|
|
1050
|
+
"TND"
|
|
1051
|
+
]);
|
|
1052
|
+
function fractionDigits(currency) {
|
|
1053
|
+
const code = currency.toUpperCase();
|
|
1054
|
+
if (ZERO_DECIMAL.has(code)) return 0;
|
|
1055
|
+
if (THREE_DECIMAL.has(code)) return 3;
|
|
1056
|
+
return 2;
|
|
1057
|
+
}
|
|
1058
|
+
function fromMinorUnits(minor, currency) {
|
|
1059
|
+
return minor / 10 ** fractionDigits(currency);
|
|
1060
|
+
}
|
|
1061
|
+
function toMinorUnits(amount, currency) {
|
|
1062
|
+
return Math.round(amount * 10 ** fractionDigits(currency));
|
|
1063
|
+
}
|
|
1064
|
+
function formatPrice(minor, currency) {
|
|
1065
|
+
if (!Number.isFinite(minor)) return "";
|
|
1066
|
+
const code = currency ?? "USD";
|
|
1067
|
+
const amount = fromMinorUnits(minor, code);
|
|
1068
|
+
try {
|
|
1069
|
+
return new Intl.NumberFormat(void 0, {
|
|
1070
|
+
style: "currency",
|
|
1071
|
+
currency: code
|
|
1072
|
+
}).format(amount);
|
|
1073
|
+
} catch {
|
|
1074
|
+
return `${amount.toFixed(fractionDigits(code))} ${code}`;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
function deriveAxes(variants) {
|
|
1078
|
+
const axes = /* @__PURE__ */ new Map();
|
|
1079
|
+
for (const variant of variants) {
|
|
1080
|
+
for (const option of variant.selectedOptions) {
|
|
1081
|
+
if (!axes.has(option.name)) axes.set(option.name, /* @__PURE__ */ new Set());
|
|
1082
|
+
axes.get(option.name).add(option.value);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
return [...axes.entries()].map(([name, values]) => ({
|
|
1086
|
+
name,
|
|
1087
|
+
values: [...values]
|
|
1088
|
+
}));
|
|
1089
|
+
}
|
|
1090
|
+
function matchVariant(variants, selections) {
|
|
1091
|
+
return variants.find(
|
|
1092
|
+
(variant) => variant.selectedOptions.every(
|
|
1093
|
+
(opt) => selections[opt.name] === opt.value
|
|
1094
|
+
)
|
|
1095
|
+
) ?? null;
|
|
1096
|
+
}
|
|
1097
|
+
function ProductComponent({ content }) {
|
|
1098
|
+
const c = content;
|
|
1099
|
+
const { fetchProduct, addToCart } = useCart();
|
|
1100
|
+
const [product, setProduct] = react.useState(null);
|
|
1101
|
+
const [loading, setLoading] = react.useState(true);
|
|
1102
|
+
const [selections, setSelections] = react.useState({});
|
|
1103
|
+
const [adding, setAdding] = react.useState(false);
|
|
1104
|
+
const [added, setAdded] = react.useState(false);
|
|
1105
|
+
const [addError, setAddError] = react.useState(null);
|
|
1106
|
+
const modelSlug = c.modelSlug ?? "products";
|
|
1107
|
+
const slugField = c.slugField ?? "slug";
|
|
1108
|
+
const slug = c.slug ?? "";
|
|
1109
|
+
react.useEffect(() => {
|
|
1110
|
+
let active = true;
|
|
1111
|
+
setLoading(true);
|
|
1112
|
+
void (async () => {
|
|
1113
|
+
try {
|
|
1114
|
+
const result = slug ? await fetchProduct(modelSlug, { [slugField]: slug }) : null;
|
|
1115
|
+
if (active) setProduct(result);
|
|
1116
|
+
} finally {
|
|
1117
|
+
if (active) setLoading(false);
|
|
1118
|
+
}
|
|
1119
|
+
})();
|
|
1120
|
+
return () => {
|
|
1121
|
+
active = false;
|
|
1122
|
+
};
|
|
1123
|
+
}, [fetchProduct, modelSlug, slugField, slug]);
|
|
1124
|
+
const axes = react.useMemo(
|
|
1125
|
+
() => product ? deriveAxes(product.variants) : [],
|
|
1126
|
+
[product]
|
|
1127
|
+
);
|
|
1128
|
+
const variant = react.useMemo(
|
|
1129
|
+
() => product ? matchVariant(product.variants, selections) : null,
|
|
1130
|
+
[product, selections]
|
|
1131
|
+
);
|
|
1132
|
+
if (loading) {
|
|
1133
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cmssy-product": "loading", children: "Loading\u2026" });
|
|
1134
|
+
}
|
|
1135
|
+
if (!product) {
|
|
1136
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cmssy-product": "not-found", children: "Product not found" });
|
|
1137
|
+
}
|
|
1138
|
+
const data = product.data;
|
|
1139
|
+
const name = String(data[c.nameField ?? "name"] ?? "");
|
|
1140
|
+
const imageUrl = c.imageField ? data[c.imageField] : null;
|
|
1141
|
+
const hasVariants = product.variants.length > 0;
|
|
1142
|
+
const currency = data.currency ?? "USD";
|
|
1143
|
+
const priceMinor = hasVariants ? variant?.price : toMinorUnits(Number(data[c.priceField ?? "price"] ?? 0), currency);
|
|
1144
|
+
const showPrice = priceMinor != null && Number.isFinite(priceMinor);
|
|
1145
|
+
const allAxesSelected = axes.every((axis) => selections[axis.name]);
|
|
1146
|
+
const outOfStock = variant != null && variant.inventory != null && variant.inventory <= 0;
|
|
1147
|
+
const canAdd = hasVariants ? Boolean(variant) && !outOfStock : true;
|
|
1148
|
+
async function onAdd() {
|
|
1149
|
+
if (!product) return;
|
|
1150
|
+
setAdding(true);
|
|
1151
|
+
setAdded(false);
|
|
1152
|
+
setAddError(null);
|
|
1153
|
+
try {
|
|
1154
|
+
await addToCart(
|
|
1155
|
+
product.id,
|
|
1156
|
+
1,
|
|
1157
|
+
hasVariants ? { variantSelections: selections } : void 0
|
|
1158
|
+
);
|
|
1159
|
+
setAdded(true);
|
|
1160
|
+
} catch (err) {
|
|
1161
|
+
setAddError(err instanceof Error ? err.message : "Could not add to cart");
|
|
1162
|
+
} finally {
|
|
1163
|
+
setAdding(false);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cmssy-product": product.id, children: [
|
|
1167
|
+
imageUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: imageUrl, alt: name }) : null,
|
|
1168
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { "data-cmssy-product-name": true, children: name }),
|
|
1169
|
+
showPrice ? /* @__PURE__ */ jsxRuntime.jsx("p", { "data-cmssy-product-price": true, children: formatPrice(priceMinor, currency) }) : null,
|
|
1170
|
+
axes.map((axis) => /* @__PURE__ */ jsxRuntime.jsxs("label", { "data-cmssy-variant-axis": axis.name, children: [
|
|
1171
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: axis.name }),
|
|
1172
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1173
|
+
"select",
|
|
1174
|
+
{
|
|
1175
|
+
value: selections[axis.name] ?? "",
|
|
1176
|
+
onChange: (e) => setSelections((prev) => ({
|
|
1177
|
+
...prev,
|
|
1178
|
+
[axis.name]: e.target.value
|
|
1179
|
+
})),
|
|
1180
|
+
children: [
|
|
1181
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u2014" }),
|
|
1182
|
+
axis.values.map((v) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: v, children: v }, v))
|
|
1183
|
+
]
|
|
1184
|
+
}
|
|
1185
|
+
)
|
|
1186
|
+
] }, axis.name)),
|
|
1187
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1188
|
+
"button",
|
|
1189
|
+
{
|
|
1190
|
+
type: "button",
|
|
1191
|
+
onClick: onAdd,
|
|
1192
|
+
disabled: adding || !canAdd || hasVariants && !allAxesSelected,
|
|
1193
|
+
"data-cmssy-add-to-cart": true,
|
|
1194
|
+
children: adding ? "Adding\u2026" : outOfStock ? "Out of stock" : added ? "Added" : "Add to cart"
|
|
1195
|
+
}
|
|
1196
|
+
),
|
|
1197
|
+
addError ? /* @__PURE__ */ jsxRuntime.jsx("p", { "data-cmssy-product-error": true, children: addError }) : null
|
|
1198
|
+
] });
|
|
1199
|
+
}
|
|
1200
|
+
var productBlock = defineBlock({
|
|
1201
|
+
type: "product",
|
|
1202
|
+
label: "Product",
|
|
1203
|
+
category: "Commerce",
|
|
1204
|
+
props: {
|
|
1205
|
+
modelSlug: fields.singleLine({ label: "Model slug" }),
|
|
1206
|
+
slugField: fields.singleLine({ label: "Slug field" }),
|
|
1207
|
+
slug: fields.singleLine({ label: "Product slug" }),
|
|
1208
|
+
nameField: fields.singleLine({ label: "Name field" }),
|
|
1209
|
+
priceField: fields.singleLine({ label: "Price field" }),
|
|
1210
|
+
imageField: fields.singleLine({ label: "Image field" })
|
|
1211
|
+
},
|
|
1212
|
+
component: ProductComponent
|
|
1213
|
+
});
|
|
1214
|
+
function CartComponent() {
|
|
1215
|
+
const {
|
|
1216
|
+
cart,
|
|
1217
|
+
loading,
|
|
1218
|
+
error,
|
|
1219
|
+
updateItem,
|
|
1220
|
+
removeItem,
|
|
1221
|
+
applyDiscount,
|
|
1222
|
+
removeDiscount
|
|
1223
|
+
} = useCart();
|
|
1224
|
+
const [code, setCode] = react.useState("");
|
|
1225
|
+
const [busy, setBusy] = react.useState(false);
|
|
1226
|
+
if (loading) {
|
|
1227
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cmssy-cart": "loading", children: "Loading\u2026" });
|
|
1228
|
+
}
|
|
1229
|
+
if (!cart || cart.items.length === 0) {
|
|
1230
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cmssy-cart": "empty", children: "Your cart is empty." });
|
|
1231
|
+
}
|
|
1232
|
+
const currency = cart.currency ?? "USD";
|
|
1233
|
+
async function withBusy(fn) {
|
|
1234
|
+
setBusy(true);
|
|
1235
|
+
try {
|
|
1236
|
+
await fn();
|
|
1237
|
+
} catch {
|
|
1238
|
+
setBusy(false);
|
|
1239
|
+
} finally {
|
|
1240
|
+
setBusy(false);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cmssy-cart": cart.id, children: [
|
|
1244
|
+
error ? /* @__PURE__ */ jsxRuntime.jsx("p", { "data-cmssy-cart-error": true, children: error }) : null,
|
|
1245
|
+
/* @__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: [
|
|
1246
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-cmssy-item-name": true, children: item.snapshot.name }),
|
|
1247
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1248
|
+
"input",
|
|
1249
|
+
{
|
|
1250
|
+
type: "number",
|
|
1251
|
+
min: "1",
|
|
1252
|
+
value: item.quantity,
|
|
1253
|
+
disabled: busy,
|
|
1254
|
+
onChange: (e) => {
|
|
1255
|
+
const qty = Number(e.target.value);
|
|
1256
|
+
if (Number.isInteger(qty) && qty >= 1) {
|
|
1257
|
+
void withBusy(() => updateItem(item.id, qty));
|
|
1258
|
+
}
|
|
1259
|
+
},
|
|
1260
|
+
"data-cmssy-item-qty": true
|
|
1261
|
+
}
|
|
1262
|
+
),
|
|
1263
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-cmssy-item-price": true, children: formatPrice(item.snapshot.price * item.quantity, currency) }),
|
|
1264
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1265
|
+
"button",
|
|
1266
|
+
{
|
|
1267
|
+
type: "button",
|
|
1268
|
+
disabled: busy,
|
|
1269
|
+
onClick: () => void withBusy(() => removeItem(item.id)),
|
|
1270
|
+
"data-cmssy-item-remove": true,
|
|
1271
|
+
children: "Remove"
|
|
1272
|
+
}
|
|
1273
|
+
)
|
|
1274
|
+
] }, item.id)) }),
|
|
1275
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { "data-cmssy-cart-discount": true, children: cart.appliedDiscount ? /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
1276
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: cart.appliedDiscount.code }),
|
|
1277
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1278
|
+
"button",
|
|
1279
|
+
{
|
|
1280
|
+
type: "button",
|
|
1281
|
+
disabled: busy,
|
|
1282
|
+
onClick: () => void withBusy(() => removeDiscount()),
|
|
1283
|
+
children: "Remove"
|
|
1284
|
+
}
|
|
1285
|
+
)
|
|
1286
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
1287
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1288
|
+
"input",
|
|
1289
|
+
{
|
|
1290
|
+
type: "text",
|
|
1291
|
+
value: code,
|
|
1292
|
+
placeholder: "Discount code",
|
|
1293
|
+
onChange: (e) => setCode(e.target.value),
|
|
1294
|
+
disabled: busy
|
|
1295
|
+
}
|
|
1296
|
+
),
|
|
1297
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1298
|
+
"button",
|
|
1299
|
+
{
|
|
1300
|
+
type: "button",
|
|
1301
|
+
disabled: busy || !code.trim(),
|
|
1302
|
+
onClick: () => void withBusy(async () => {
|
|
1303
|
+
await applyDiscount(code.trim());
|
|
1304
|
+
setCode("");
|
|
1305
|
+
}),
|
|
1306
|
+
children: "Apply"
|
|
1307
|
+
}
|
|
1308
|
+
)
|
|
1309
|
+
] }) }),
|
|
1310
|
+
/* @__PURE__ */ jsxRuntime.jsxs("dl", { "data-cmssy-cart-totals": true, children: [
|
|
1311
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1312
|
+
/* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Subtotal" }),
|
|
1313
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { children: formatPrice(cart.subtotal, currency) })
|
|
1314
|
+
] }),
|
|
1315
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1316
|
+
/* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Total" }),
|
|
1317
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { "data-cmssy-cart-total": true, children: formatPrice(cart.discountedTotal, currency) })
|
|
1318
|
+
] })
|
|
1319
|
+
] })
|
|
1320
|
+
] });
|
|
1321
|
+
}
|
|
1322
|
+
var cartBlock = defineBlock({
|
|
1323
|
+
type: "cart",
|
|
1324
|
+
label: "Cart",
|
|
1325
|
+
category: "Commerce",
|
|
1326
|
+
props: {},
|
|
1327
|
+
component: CartComponent
|
|
1328
|
+
});
|
|
1329
|
+
var EMAIL_RE = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
|
|
1330
|
+
function CheckoutComponent({ content }) {
|
|
1331
|
+
const c = content;
|
|
1332
|
+
const { cart, loading, checkout } = useCart();
|
|
1333
|
+
const [email, setEmail] = react.useState("");
|
|
1334
|
+
const [busy, setBusy] = react.useState(false);
|
|
1335
|
+
const [error, setError] = react.useState(null);
|
|
1336
|
+
const [order, setOrder] = react.useState(null);
|
|
1337
|
+
if (order) {
|
|
1338
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cmssy-checkout": "done", children: [
|
|
1339
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { children: c.successMessage ?? "Order placed - awaiting payment." }),
|
|
1340
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { "data-cmssy-order-id": true, children: order.id }),
|
|
1341
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { "data-cmssy-order-total": true, children: formatPrice(order.total, order.currency) })
|
|
1342
|
+
] });
|
|
1343
|
+
}
|
|
1344
|
+
if (loading) {
|
|
1345
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cmssy-checkout": "loading", children: "Loading\u2026" });
|
|
1346
|
+
}
|
|
1347
|
+
if (!cart || cart.items.length === 0) {
|
|
1348
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cmssy-checkout": "empty", children: "Your cart is empty." });
|
|
1349
|
+
}
|
|
1350
|
+
const emailValid = EMAIL_RE.test(email.trim());
|
|
1351
|
+
async function onSubmit() {
|
|
1352
|
+
setBusy(true);
|
|
1353
|
+
setError(null);
|
|
1354
|
+
try {
|
|
1355
|
+
setOrder(await checkout(email.trim()));
|
|
1356
|
+
} catch (err) {
|
|
1357
|
+
setError(err instanceof Error ? err.message : "Checkout failed");
|
|
1358
|
+
} finally {
|
|
1359
|
+
setBusy(false);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1363
|
+
"form",
|
|
1364
|
+
{
|
|
1365
|
+
"data-cmssy-checkout": cart.id,
|
|
1366
|
+
onSubmit: (e) => {
|
|
1367
|
+
e.preventDefault();
|
|
1368
|
+
if (emailValid && !busy) void onSubmit();
|
|
1369
|
+
},
|
|
1370
|
+
children: [
|
|
1371
|
+
error ? /* @__PURE__ */ jsxRuntime.jsx("p", { "data-cmssy-checkout-error": true, children: error }) : null,
|
|
1372
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
1373
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Email" }),
|
|
1374
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1375
|
+
"input",
|
|
1376
|
+
{
|
|
1377
|
+
type: "email",
|
|
1378
|
+
value: email,
|
|
1379
|
+
onChange: (e) => setEmail(e.target.value),
|
|
1380
|
+
required: true,
|
|
1381
|
+
"data-cmssy-checkout-email": true
|
|
1382
|
+
}
|
|
1383
|
+
)
|
|
1384
|
+
] }),
|
|
1385
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { "data-cmssy-checkout-total": true, children: formatPrice(cart.discountedTotal, cart.currency ?? "USD") }),
|
|
1386
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1387
|
+
"button",
|
|
1388
|
+
{
|
|
1389
|
+
type: "submit",
|
|
1390
|
+
disabled: busy || !emailValid,
|
|
1391
|
+
"data-cmssy-checkout-submit": true,
|
|
1392
|
+
children: busy ? "Placing order\u2026" : "Place order"
|
|
1393
|
+
}
|
|
1394
|
+
)
|
|
1395
|
+
]
|
|
1396
|
+
}
|
|
1397
|
+
);
|
|
1398
|
+
}
|
|
1399
|
+
var checkoutBlock = defineBlock({
|
|
1400
|
+
type: "checkout",
|
|
1401
|
+
label: "Checkout",
|
|
1402
|
+
category: "Commerce",
|
|
1403
|
+
props: {
|
|
1404
|
+
successMessage: fields.singleLine({ label: "Success message" })
|
|
1405
|
+
},
|
|
1406
|
+
component: CheckoutComponent
|
|
1407
|
+
});
|
|
844
1408
|
|
|
845
1409
|
exports.CmssyAuthProvider = CmssyAuthProvider;
|
|
1410
|
+
exports.CmssyCommerceProvider = CmssyCommerceProvider;
|
|
846
1411
|
exports.CmssyEditableLayout = CmssyEditableLayout;
|
|
847
1412
|
exports.CmssyEditablePage = CmssyEditablePage;
|
|
848
1413
|
exports.CmssyLazyEditor = CmssyLazyEditor;
|
|
849
1414
|
exports.CmssyLazyLayout = CmssyLazyLayout;
|
|
1415
|
+
exports.cartBlock = cartBlock;
|
|
1416
|
+
exports.checkoutBlock = checkoutBlock;
|
|
1417
|
+
exports.formatPrice = formatPrice;
|
|
1418
|
+
exports.fractionDigits = fractionDigits;
|
|
1419
|
+
exports.fromMinorUnits = fromMinorUnits;
|
|
1420
|
+
exports.productBlock = productBlock;
|
|
1421
|
+
exports.toMinorUnits = toMinorUnits;
|
|
1422
|
+
exports.useCart = useCart;
|
|
850
1423
|
exports.useCmssyUser = useCmssyUser;
|
|
851
1424
|
exports.useEditBridge = useEditBridge;
|
package/dist/client.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { B as BlockSchema, a as BlockMeta, C as CmssyPageData, b as BlockDefinition, c as CmssyFormDefinition, d as CmssyLayoutGroup } from './
|
|
2
|
+
import { B as BlockSchema, a as BlockMeta, C as CmssyPageData, b as BlockDefinition, c as CmssyFormDefinition, d as CmssyLayoutGroup, e as CmssyCart, f as CmssyOrder, g as CmssyProduct } from './commerce-queries-DV7PZlKS.cjs';
|
|
3
|
+
export { h as CmssyCartDiscount, i as CmssyCartItem, j as CmssyCartItemSnapshot, k as CmssyProductVariant } from './commerce-queries-DV7PZlKS.cjs';
|
|
3
4
|
import { ReactNode } from 'react';
|
|
4
5
|
|
|
5
6
|
interface EditBridgeConfig {
|
|
@@ -104,4 +105,40 @@ interface CmssyAuthProviderProps {
|
|
|
104
105
|
declare function CmssyAuthProvider({ children, basePath, initialUser, }: CmssyAuthProviderProps): react_jsx_runtime.JSX.Element;
|
|
105
106
|
declare function useCmssyUser(): CmssyAuthState;
|
|
106
107
|
|
|
107
|
-
|
|
108
|
+
interface CmssyAddToCartOptions {
|
|
109
|
+
variantSelections?: Record<string, string>;
|
|
110
|
+
notes?: string;
|
|
111
|
+
}
|
|
112
|
+
interface CmssyCommerceState {
|
|
113
|
+
cart: CmssyCart | null;
|
|
114
|
+
loading: boolean;
|
|
115
|
+
error: string | null;
|
|
116
|
+
addToCart(recordId: string, quantity?: number, options?: CmssyAddToCartOptions): Promise<void>;
|
|
117
|
+
updateItem(itemId: string, quantity: number): Promise<void>;
|
|
118
|
+
removeItem(itemId: string): Promise<void>;
|
|
119
|
+
clearCart(): Promise<void>;
|
|
120
|
+
applyDiscount(code: string): Promise<void>;
|
|
121
|
+
removeDiscount(): Promise<void>;
|
|
122
|
+
checkout(customerEmail: string): Promise<CmssyOrder>;
|
|
123
|
+
refresh(): Promise<void>;
|
|
124
|
+
fetchProduct(modelSlug: string, filter: Record<string, unknown>): Promise<CmssyProduct | null>;
|
|
125
|
+
}
|
|
126
|
+
interface CmssyCommerceProviderProps {
|
|
127
|
+
children: ReactNode;
|
|
128
|
+
basePath?: string;
|
|
129
|
+
}
|
|
130
|
+
declare function CmssyCommerceProvider({ children, basePath, }: CmssyCommerceProviderProps): react_jsx_runtime.JSX.Element;
|
|
131
|
+
declare function useCart(): CmssyCommerceState;
|
|
132
|
+
|
|
133
|
+
declare const productBlock: BlockDefinition;
|
|
134
|
+
|
|
135
|
+
declare const cartBlock: BlockDefinition;
|
|
136
|
+
|
|
137
|
+
declare const checkoutBlock: BlockDefinition;
|
|
138
|
+
|
|
139
|
+
declare function fractionDigits(currency: string): number;
|
|
140
|
+
declare function fromMinorUnits(minor: number, currency: string): number;
|
|
141
|
+
declare function toMinorUnits(amount: number, currency: string): number;
|
|
142
|
+
declare function formatPrice(minor: number, currency: string | null | undefined): string;
|
|
143
|
+
|
|
144
|
+
export { type CmssyAddToCartOptions, type CmssyAuthActionResult, CmssyAuthProvider, type CmssyAuthProviderProps, type CmssyAuthState, type CmssyAuthUser, CmssyCart, CmssyCommerceProvider, type CmssyCommerceProviderProps, type CmssyCommerceState, CmssyEditableLayout, type CmssyEditableLayoutProps, CmssyEditablePage, type CmssyEditablePageProps, CmssyLazyEditor, type CmssyLazyEditorProps, CmssyLazyLayout, type CmssyLazyLayoutProps, CmssyOrder, CmssyProduct, type EditBridgeConfig, type EditBridgeState, type PatchMap, cartBlock, checkoutBlock, formatPrice, fractionDigits, fromMinorUnits, productBlock, toMinorUnits, useCart, useCmssyUser, useEditBridge };
|