@sazito/checkout 0.1.1 → 0.2.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/README.md +4 -0
- package/dist/chunks/{labels-BDeABGTD.js → labels-ByA_yGKl.js} +131 -29
- package/dist/chunks/labels-ByA_yGKl.js.map +1 -0
- package/dist/chunks/{labels-CWfNSAYm.cjs → labels-DJ_RnkCQ.cjs} +132 -28
- package/dist/chunks/labels-DJ_RnkCQ.cjs.map +1 -0
- package/dist/chunks/{use-checkout-CKFla5or.cjs → use-checkout-CQBSONj1.cjs} +2 -2
- package/dist/chunks/{use-checkout-CKFla5or.cjs.map → use-checkout-CQBSONj1.cjs.map} +1 -1
- package/dist/chunks/{use-checkout-DIkxFBnD.js → use-checkout-gAjnCzae.js} +2 -2
- package/dist/chunks/{use-checkout-DIkxFBnD.js.map → use-checkout-gAjnCzae.js.map} +1 -1
- package/dist/core/index.cjs +3 -1
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +17 -4
- package/dist/core/index.d.ts +17 -4
- package/dist/core/index.js +1 -1
- package/dist/next/index.cjs +29 -11
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +9 -0
- package/dist/next/index.d.ts +9 -0
- package/dist/next/index.js +31 -13
- package/dist/next/index.js.map +1 -1
- package/dist/react/index.cjs +2 -2
- package/dist/react/index.d.cts +9 -0
- package/dist/react/index.d.ts +9 -0
- package/dist/react/index.js +2 -2
- package/dist/styles.css +147 -29
- package/package.json +1 -1
- package/dist/chunks/labels-BDeABGTD.js.map +0 -1
- package/dist/chunks/labels-CWfNSAYm.cjs.map +0 -1
package/dist/next/index.cjs
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
5
|
var react = require('react');
|
|
6
|
-
var useCheckout = require('../chunks/use-checkout-
|
|
7
|
-
var labels = require('../chunks/labels-
|
|
6
|
+
var useCheckout = require('../chunks/use-checkout-CQBSONj1.cjs');
|
|
7
|
+
var labels = require('../chunks/labels-DJ_RnkCQ.cjs');
|
|
8
8
|
require('@sazito/client-sdk');
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -140,6 +140,7 @@ function ClipboardIcon() {
|
|
|
140
140
|
|
|
141
141
|
function OrderSummary() {
|
|
142
142
|
const { state, summary, money, t } = useCheckout.useCheckout();
|
|
143
|
+
const updating = state.flags.updatingCart;
|
|
143
144
|
const percentSign = state.locale === 'fa' ? '٪' : '%';
|
|
144
145
|
const lineLabels = {
|
|
145
146
|
subtotal: t.subtotal,
|
|
@@ -148,14 +149,14 @@ function OrderSummary() {
|
|
|
148
149
|
credit: t.credit,
|
|
149
150
|
vat: t.vat
|
|
150
151
|
};
|
|
151
|
-
return (jsxRuntime.jsxs("aside", { className:
|
|
152
|
+
return (jsxRuntime.jsxs("aside", { className: `szc-summary${updating ? ' szc-summary--loading' : ''}`, "aria-busy": updating, children: [jsxRuntime.jsxs("div", { className: "szc-summary__total-head", children: [jsxRuntime.jsx("span", { className: "szc-summary__total-label", children: t.totalAmount }), jsxRuntime.jsx("span", { className: "szc-summary__total-value", children: money(summary.total) })] }), jsxRuntime.jsx("dl", { className: "szc-summary__lines", children: summary.lines.map((line) => (jsxRuntime.jsxs("div", { className: `szc-summary__line${line.key === 'discount' ? ' szc-summary__line--discount' : ''}`, children: [jsxRuntime.jsxs("dt", { children: [lineLabels[line.key], (line.percent ?? 0) > 0
|
|
152
153
|
? ` (${labels.formatPercent(line.percent, state.locale)}${percentSign})`
|
|
153
|
-
: ''] }), jsxRuntime.jsx("dd", { className: line.negative ? 'szc-summary__line--neg' : undefined, children: line.free ? (jsxRuntime.jsx("span", { className: "szc-summary__free", children: t.free })) : (`${line.negative ? '−' : ''}${money(line.amount)}`) })] }, line.key))) }), jsxRuntime.jsxs("div", { className: "szc-summary__grand", children: [jsxRuntime.jsx("span", { children: t.total }), jsxRuntime.jsx("span", { children: money(summary.total) })] })] }));
|
|
154
|
+
: ''] }), jsxRuntime.jsx("dd", { className: line.negative ? 'szc-summary__line--neg' : undefined, children: line.free ? (jsxRuntime.jsx("span", { className: "szc-summary__free", children: t.free })) : (`${line.negative ? '−' : ''}${money(line.amount)}`) })] }, line.key))) }), jsxRuntime.jsxs("div", { className: "szc-summary__grand", children: [jsxRuntime.jsx("span", { children: t.total }), jsxRuntime.jsx("span", { children: money(summary.total) })] }), updating ? (jsxRuntime.jsxs("div", { className: "szc-summary__loading", role: "status", "aria-live": "polite", children: [jsxRuntime.jsx(Spinner, { className: "szc-summary__spinner" }), jsxRuntime.jsx("span", { className: "szc-visually-hidden", children: t.loading })] })) : null] }));
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
const PERSIAN_DIGITS = '۰۱۲۳۴۵۶۷۸۹';
|
|
157
158
|
const ARABIC_DIGITS = '٠١٢٣٤٥٦٧٨٩';
|
|
158
|
-
const QUANTITY_DEBOUNCE_MS =
|
|
159
|
+
const QUANTITY_DEBOUNCE_MS = 500;
|
|
159
160
|
function toLatinDigits(value) {
|
|
160
161
|
return value
|
|
161
162
|
.replace(/[۰-۹]/g, (digit) => String(PERSIAN_DIGITS.indexOf(digit)))
|
|
@@ -189,6 +190,7 @@ function CartStep({ continueShoppingUrl, renderEmpty, }) {
|
|
|
189
190
|
const { state, actions, money, t } = useCheckout.useCheckout();
|
|
190
191
|
const cart = state.cart;
|
|
191
192
|
const busy = state.flags.updatingCart;
|
|
193
|
+
const newestCartItems = react.useMemo(() => labels.sortCartItemsNewestFirst(cart?.items ?? []), [cart?.items]);
|
|
192
194
|
const invoiceItemsByVariant = new Map((state.invoice?.items ?? []).map((item) => [item.productVariantId, item]));
|
|
193
195
|
if (!cart || cart.items.length === 0) {
|
|
194
196
|
if (renderEmpty) {
|
|
@@ -196,7 +198,7 @@ function CartStep({ continueShoppingUrl, renderEmpty, }) {
|
|
|
196
198
|
}
|
|
197
199
|
return (jsxRuntime.jsx("section", { className: "szc-empty", role: "status", "aria-live": "polite", children: jsxRuntime.jsxs("div", { className: "szc-empty__card", children: [jsxRuntime.jsx("span", { className: "szc-empty__badge", children: jsxRuntime.jsx(CartIcon, {}) }), jsxRuntime.jsxs("div", { className: "szc-empty__copy", children: [jsxRuntime.jsx("h3", { className: "szc-empty__title", children: t.cartEmpty }), jsxRuntime.jsx("p", { className: "szc-empty__hint", children: t.cartEmptyHint })] }), continueShoppingUrl ? (jsxRuntime.jsx(Button, { className: "szc-empty__cta", variant: "primary", onClick: () => { window.location.href = continueShoppingUrl; }, children: t.continueShopping })) : null] }) }));
|
|
198
200
|
}
|
|
199
|
-
return (jsxRuntime.jsx("section", { className: "szc-step-panel", children: jsxRuntime.jsx("ul", { className: "szc-cart-list", children:
|
|
201
|
+
return (jsxRuntime.jsx("section", { className: "szc-step-panel", children: jsxRuntime.jsx("ul", { className: "szc-cart-list", children: newestCartItems.map((item) => {
|
|
200
202
|
const max = item.product.maxOrderQuantity;
|
|
201
203
|
const min = item.product.minOrderQuantity ?? 1;
|
|
202
204
|
const invoiceItem = invoiceItemsByVariant.get(item.product.variantId);
|
|
@@ -215,6 +217,7 @@ function CartStep({ continueShoppingUrl, renderEmpty, }) {
|
|
|
215
217
|
function QuantityControl({ value, min, max, label, onChange }) {
|
|
216
218
|
const [draft, setDraft] = react.useState(() => labels.toPersianDigits(String(value)));
|
|
217
219
|
const [pending, setPending] = react.useState(false);
|
|
220
|
+
const [updating, setUpdating] = react.useState(false);
|
|
218
221
|
const draftRef = react.useRef(draft);
|
|
219
222
|
const pendingValueRef = react.useRef(null);
|
|
220
223
|
const timerRef = react.useRef(null);
|
|
@@ -249,11 +252,13 @@ function QuantityControl({ value, min, max, label, onChange }) {
|
|
|
249
252
|
return;
|
|
250
253
|
}
|
|
251
254
|
inFlightRef.current = true;
|
|
255
|
+
setUpdating(true);
|
|
252
256
|
try {
|
|
253
257
|
await onChangeRef.current(target);
|
|
254
258
|
}
|
|
255
259
|
finally {
|
|
256
260
|
inFlightRef.current = false;
|
|
261
|
+
setUpdating(false);
|
|
257
262
|
if (pendingValueRef.current === target) {
|
|
258
263
|
pendingValueRef.current = null;
|
|
259
264
|
setPending(false);
|
|
@@ -288,7 +293,7 @@ function QuantityControl({ value, min, max, label, onChange }) {
|
|
|
288
293
|
};
|
|
289
294
|
const parsedDraft = Number(toLatinDigits(draft));
|
|
290
295
|
const displayedValue = Number.isFinite(parsedDraft) ? normalize(parsedDraft) : value;
|
|
291
|
-
return (jsxRuntime.jsxs("div", { className: `szc-qty${pending ? ' szc-qty--pending' : ''}`, "aria-busy":
|
|
296
|
+
return (jsxRuntime.jsxs("div", { className: `szc-qty${pending ? ' szc-qty--pending' : ''}`, "aria-busy": updating, children: [jsxRuntime.jsx("button", { type: "button", className: "szc-qty__btn", "aria-label": `${label} −`, disabled: updating || displayedValue <= min, onMouseDown: (event) => event.preventDefault(), onClick: () => step(-1), children: jsxRuntime.jsx(MinusIcon, {}) }), jsxRuntime.jsx("input", { className: "szc-qty__input", type: "text", inputMode: "numeric", pattern: "[\u06F0-\u06F9\u0660-\u06690-9]*", role: "spinbutton", value: draft, "aria-label": label, "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": displayedValue, disabled: updating, onChange: (event) => {
|
|
292
297
|
const digits = toLatinDigits(event.target.value).replace(/\D/g, '');
|
|
293
298
|
if (!digits) {
|
|
294
299
|
if (timerRef.current)
|
|
@@ -319,7 +324,7 @@ function QuantityControl({ value, min, max, label, onChange }) {
|
|
|
319
324
|
event.preventDefault();
|
|
320
325
|
step(-1);
|
|
321
326
|
}
|
|
322
|
-
} }), jsxRuntime.jsx("button", { type: "button", className: "szc-qty__btn", "aria-label": `${label} +`, disabled: max != null && displayedValue >= max, onMouseDown: (event) => event.preventDefault(), onClick: () => step(1), children: jsxRuntime.jsx(PlusIcon, {}) })] }));
|
|
327
|
+
} }), jsxRuntime.jsx("button", { type: "button", className: "szc-qty__btn", "aria-label": `${label} +`, disabled: updating || (max != null && displayedValue >= max), onMouseDown: (event) => event.preventDefault(), onClick: () => step(1), children: jsxRuntime.jsx(PlusIcon, {}) })] }));
|
|
323
328
|
}
|
|
324
329
|
function MinusIcon() {
|
|
325
330
|
return (jsxRuntime.jsx("svg", { viewBox: "0 0 16 16", width: "14", height: "14", fill: "none", "aria-hidden": "true", children: jsxRuntime.jsx("path", { d: "M3.5 8h9", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }));
|
|
@@ -366,6 +371,8 @@ function ShippingStep() {
|
|
|
366
371
|
const { addressForm, regions, shippingGroups } = state;
|
|
367
372
|
const [touched, setTouched] = react.useState({});
|
|
368
373
|
const needsShipping = state.invoice?.needsShipping ?? true;
|
|
374
|
+
const physicalShippingGroups = shippingGroups.filter((group) => !labels.isDigitalServiceGroup(group));
|
|
375
|
+
const physicalItemCount = Math.max(0, (state.invoice?.items.length ?? 0) - digitalItems.length);
|
|
369
376
|
const touch = react.useCallback((key) => {
|
|
370
377
|
setTouched((prev) => ({ ...prev, [key]: true }));
|
|
371
378
|
}, []);
|
|
@@ -388,7 +395,7 @@ function ShippingStep() {
|
|
|
388
395
|
}, placeholder: t.selectRegion, options: regions.map((r) => ({ value: r.id, label: r.name })) }), jsxRuntime.jsx(EnhancedSelect, { id: "szc-city", label: t.city, required: true, optionalLabel: t.optional, value: addressForm.cityId ?? '', disabled: !selectedRegion, error: errors.cityId, onChange: (v) => {
|
|
389
396
|
actions.setAddressField('cityId', v ? Number(v) : null);
|
|
390
397
|
touch('cityId');
|
|
391
|
-
}, placeholder: t.selectCity, options: selectedRegion?.cities.map((c) => ({ value: c.id, label: c.name })) ?? [] }), jsxRuntime.jsx(Field, { id: "szc-postal", label: t.postalCode, required: state.postalCodeMandatory, optionalLabel: t.optional, className: "szc-input--ltr", dir: "ltr", inputMode: "numeric", value: addressForm.postalCode, error: errors.postalCode, onChange: (e) => actions.setAddressField('postalCode', e.target.value), onBlur: () => touch('postalCode') }), jsxRuntime.jsx(Field, { id: "szc-phone", label: t.phoneNumber, optionalLabel: t.optional, className: "szc-input--ltr", dir: "ltr", inputMode: "tel", value: addressForm.phoneNumber, onChange: (e) => actions.setAddressField('phoneNumber', e.target.value), onBlur: () => handleBlurPhone('phoneNumber') })] }), jsxRuntime.jsx(Field, { id: "szc-address", label: t.addressLine, required: true, optionalLabel: t.optional, value: addressForm.address, error: errors.address, onChange: (e) => actions.setAddressField('address', e.target.value), onBlur: () => touch('address') }), jsxRuntime.jsx(Field, { id: "szc-description", label: t.description, optionalLabel: t.optional, value: addressForm.description, onChange: (e) => actions.setAddressField('description', e.target.value), onBlur: () => touch('description') })] })) : null] }),
|
|
398
|
+
}, placeholder: t.selectCity, options: selectedRegion?.cities.map((c) => ({ value: c.id, label: c.name })) ?? [] }), jsxRuntime.jsx(Field, { id: "szc-postal", label: t.postalCode, required: state.postalCodeMandatory, optionalLabel: t.optional, className: "szc-input--ltr", dir: "ltr", inputMode: "numeric", value: addressForm.postalCode, error: errors.postalCode, onChange: (e) => actions.setAddressField('postalCode', e.target.value), onBlur: () => touch('postalCode') }), jsxRuntime.jsx(Field, { id: "szc-phone", label: t.phoneNumber, optionalLabel: t.optional, className: "szc-input--ltr", dir: "ltr", inputMode: "tel", value: addressForm.phoneNumber, onChange: (e) => actions.setAddressField('phoneNumber', e.target.value), onBlur: () => handleBlurPhone('phoneNumber') })] }), jsxRuntime.jsx(Field, { id: "szc-address", label: t.addressLine, required: true, optionalLabel: t.optional, value: addressForm.address, error: errors.address, onChange: (e) => actions.setAddressField('address', e.target.value), onBlur: () => touch('address') }), jsxRuntime.jsx(Field, { id: "szc-description", label: t.description, optionalLabel: t.optional, value: addressForm.description, onChange: (e) => actions.setAddressField('description', e.target.value), onBlur: () => touch('description') })] })) : null] }), digitalItems.length > 0 ? (jsxRuntime.jsxs("section", { className: "szc-digital-products", "aria-labelledby": "szc-digital-title", children: [jsxRuntime.jsxs("header", { className: "szc-digital-products__head", children: [jsxRuntime.jsx("span", { className: "szc-digital-products__icon", "aria-hidden": "true", children: jsxRuntime.jsx(DigitalIcon, {}) }), jsxRuntime.jsxs("div", { className: "szc-digital-products__heading", children: [jsxRuntime.jsxs("div", { className: "szc-digital-products__title-row", children: [jsxRuntime.jsx("h2", { id: "szc-digital-title", className: "szc-digital-products__title", children: t.digitalNoShipping }), jsxRuntime.jsx("span", { className: "szc-digital-products__count", children: t.productCount(labels.toPersianDigits(String(digitalItems.length))) })] }), jsxRuntime.jsx("p", { children: t.digitalNoShippingHint })] })] }), jsxRuntime.jsx("div", { className: "szc-digital-products__list", children: digitalItems.map((item) => (jsxRuntime.jsxs("article", { className: "szc-digital-product", children: [jsxRuntime.jsxs("div", { className: "szc-digital-product__media", children: [item.image?.url ? (jsxRuntime.jsx("img", { src: item.image.url, alt: item.image.alt || item.name })) : (jsxRuntime.jsx(ProductPlaceholder, {})), jsxRuntime.jsx("span", { className: "szc-ship-product__quantity", children: labels.toPersianDigits(String(item.quantity)) })] }), jsxRuntime.jsx("strong", { className: "szc-digital-product__name", children: item.name })] }, item.id))) })] })) : null, physicalShippingGroups.length > 0 ? (jsxRuntime.jsxs("section", { className: "szc-shipping-methods", "aria-labelledby": "szc-methods-title", children: [jsxRuntime.jsx(ShippingSectionHeader, { id: "szc-methods-title", title: t.shippingMethods, icon: "shipping" }), jsxRuntime.jsx("div", { className: "szc-ship-groups", children: physicalShippingGroups.map((group) => (jsxRuntime.jsx(ShippingGroupCard, { group: group }, group.key))) })] })) : needsShipping && physicalItemCount > 0 ? (jsxRuntime.jsxs("div", { className: "szc-shipping-pending", role: "note", children: [jsxRuntime.jsx("span", { className: "szc-shipping-pending__icon", "aria-hidden": "true", children: jsxRuntime.jsx(TruckIcon, {}) }), jsxRuntime.jsxs("div", { className: "szc-shipping-pending__copy", children: [jsxRuntime.jsx("strong", { children: t.shippingMethods }), jsxRuntime.jsx("span", { children: t.shippingMethodsHint })] })] })) : null] }));
|
|
392
399
|
}
|
|
393
400
|
function ShippingSectionHeader({ id, title, icon }) {
|
|
394
401
|
return (jsxRuntime.jsxs("header", { className: "szc-shipping-section__head", children: [jsxRuntime.jsx("span", { className: "szc-shipping-section__icon", "aria-hidden": "true", children: icon === 'contact' ? jsxRuntime.jsx(ContactIcon, {}) : icon === 'address' ? jsxRuntime.jsx(AddressIcon, {}) : jsxRuntime.jsx(TruckIcon, {}) }), jsxRuntime.jsx("h2", { id: id, className: "szc-shipping-section__title", children: title })] }));
|
|
@@ -399,6 +406,9 @@ function ContactIcon() {
|
|
|
399
406
|
function AddressIcon() {
|
|
400
407
|
return (jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "none", "aria-hidden": "true", children: [jsxRuntime.jsx("path", { d: "M19 10c0 4.7-7 10-7 10S5 14.7 5 10a7 7 0 1 1 14 0Z", stroke: "currentColor", strokeWidth: "1.7", strokeLinejoin: "round" }), jsxRuntime.jsx("circle", { cx: "12", cy: "10", r: "2.25", stroke: "currentColor", strokeWidth: "1.7" })] }));
|
|
401
408
|
}
|
|
409
|
+
function DigitalIcon() {
|
|
410
|
+
return (jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", width: "22", height: "22", fill: "none", "aria-hidden": "true", children: [jsxRuntime.jsx("path", { d: "M12 3v11m0 0 4-4m-4 4-4-4", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }), jsxRuntime.jsx("path", { d: "M5 16.5v1.75A1.75 1.75 0 0 0 6.75 20h10.5A1.75 1.75 0 0 0 19 18.25V16.5", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" })] }));
|
|
411
|
+
}
|
|
402
412
|
function EnhancedSelect({ id, label, value, placeholder, options, required, optionalLabel, disabled, error, onChange }) {
|
|
403
413
|
return (jsxRuntime.jsxs("label", { className: "szc-field", htmlFor: id, children: [jsxRuntime.jsx(FieldLabel, { label: label, required: required, optionalLabel: optionalLabel }), jsxRuntime.jsxs("div", { className: "szc-select-wrap", children: [jsxRuntime.jsxs("select", { id: id, className: `szc-input szc-select-enhanced${error ? ' szc-input--error' : ''}`, value: value, required: required, disabled: disabled, onChange: (e) => onChange(e.target.value), children: [jsxRuntime.jsx("option", { value: "", children: placeholder }), options.map((opt) => (jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value)))] }), jsxRuntime.jsx("span", { className: "szc-select-chevron", "aria-hidden": "true", children: jsxRuntime.jsx(ChevronIcon, {}) })] }), error ? jsxRuntime.jsx("span", { className: "szc-field__error", children: error }) : null] }));
|
|
404
414
|
}
|
|
@@ -565,8 +575,8 @@ function shippingRateIconStyle(color) {
|
|
|
565
575
|
// the card. Render a neutral chip with a slate glyph instead.
|
|
566
576
|
if (luminance > 0.9) {
|
|
567
577
|
return {
|
|
568
|
-
'--szc-rate-color': '
|
|
569
|
-
'--szc-rate-foreground': '
|
|
578
|
+
'--szc-rate-color': 'var(--szc-shipping-neutral)',
|
|
579
|
+
'--szc-rate-foreground': 'var(--szc-shipping-neutral-foreground)'
|
|
570
580
|
};
|
|
571
581
|
}
|
|
572
582
|
const foreground = luminance > 0.62
|
|
@@ -732,6 +742,14 @@ function themeVars(theme) {
|
|
|
732
742
|
vars['--szc-danger'] = theme.danger;
|
|
733
743
|
if (theme?.success)
|
|
734
744
|
vars['--szc-success'] = theme.success;
|
|
745
|
+
if (theme?.successForeground)
|
|
746
|
+
vars['--szc-success-foreground'] = theme.successForeground;
|
|
747
|
+
if (theme?.logoBackground)
|
|
748
|
+
vars['--szc-logo-bg'] = theme.logoBackground;
|
|
749
|
+
if (theme?.shippingNeutral)
|
|
750
|
+
vars['--szc-shipping-neutral'] = theme.shippingNeutral;
|
|
751
|
+
if (theme?.shippingNeutralForeground)
|
|
752
|
+
vars['--szc-shipping-neutral-foreground'] = theme.shippingNeutralForeground;
|
|
735
753
|
if (theme?.radius != null)
|
|
736
754
|
vars['--szc-radius'] = `${theme.radius}px`;
|
|
737
755
|
if (theme?.fontFamily)
|