@shopify/ui-extensions 2026.1.1 → 2026.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/surfaces/checkout/preact/metafield.js +1 -0
- package/build/cjs/surfaces/checkout/preact/metafields.js +1 -0
- package/build/esm/surfaces/checkout/preact/metafield.mjs +1 -0
- package/build/esm/surfaces/checkout/preact/metafields.mjs +1 -0
- package/build/esnext/surfaces/checkout/preact/metafield.esnext +1 -0
- package/build/esnext/surfaces/checkout/preact/metafields.esnext +1 -0
- package/build/ts/surfaces/admin/api/action/action.doc.d.ts.map +1 -1
- package/build/ts/surfaces/admin/api/block/block.doc.d.ts.map +1 -1
- package/build/ts/surfaces/admin/api/checkout-rules/validation-settings.doc.d.ts.map +1 -1
- package/build/ts/surfaces/admin/api/customer-segment-template/customer-segment-template.doc.d.ts.map +1 -1
- package/build/ts/surfaces/admin/api/discount-function-settings/discount-function-settings.doc.d.ts.map +1 -1
- package/build/ts/surfaces/admin/api/intents/intents.doc.d.ts.map +1 -1
- package/build/ts/surfaces/admin/api/order-routing-rule/order-routing-rule.doc.d.ts.map +1 -1
- package/build/ts/surfaces/admin/api/picker/picker.doc.d.ts.map +1 -1
- package/build/ts/surfaces/admin/api/print-action/print-action.doc.d.ts.map +1 -1
- package/build/ts/surfaces/admin/api/product-configuration/product-details-configuration.doc.d.ts.map +1 -1
- package/build/ts/surfaces/admin/api/product-configuration/product-variant-details-configuration.doc.d.ts.map +1 -1
- package/build/ts/surfaces/admin/api/purchase-options-card-action.doc.d.ts.map +1 -1
- package/build/ts/surfaces/admin/api/resource-picker/resource-picker.doc.d.ts.map +1 -1
- package/build/ts/surfaces/admin/api/should-render/should-render.doc.d.ts.map +1 -1
- package/build/ts/surfaces/admin/api/standard/standard.doc.d.ts.map +1 -1
- package/build/ts/surfaces/admin/api/standard/storage.d.ts +1 -1
- package/build/ts/surfaces/admin/extension-targets.d.ts +1 -1
- package/build/ts/surfaces/checkout/api/address-autocomplete/standard.d.ts +5 -0
- package/build/ts/surfaces/checkout/api/address-autocomplete/standard.d.ts.map +1 -1
- package/build/ts/surfaces/checkout/api/standard/standard.d.ts +4 -2
- package/build/ts/surfaces/checkout/api/standard/standard.d.ts.map +1 -1
- package/build/ts/surfaces/checkout/preact/metafield.d.ts +1 -0
- package/build/ts/surfaces/checkout/preact/metafield.d.ts.map +1 -1
- package/build/ts/surfaces/checkout/preact/metafields.d.ts +1 -0
- package/build/ts/surfaces/checkout/preact/metafields.d.ts.map +1 -1
- package/build/ts/surfaces/point-of-sale/components/Badge/Badge.doc.d.ts.map +1 -1
- package/build/ts/surfaces/point-of-sale/components/EmptyState.d.ts +44 -34
- package/build/ts/surfaces/point-of-sale/components/Icon/Icon.doc.d.ts.map +1 -1
- package/build/ts/surfaces/point-of-sale/components/Image/Image.doc.d.ts.map +1 -1
- package/build/ts/surfaces/point-of-sale/components/Link/Link.doc.d.ts.map +1 -1
- package/build/ts/surfaces/point-of-sale/components/Modal/Modal.doc.d.ts.map +1 -1
- package/build/ts/surfaces/point-of-sale/components/Page/Page.doc.d.ts.map +1 -1
- package/build/ts/surfaces/point-of-sale/components/Stack/Stack.doc.d.ts.map +1 -1
- package/build/ts/surfaces/point-of-sale/components/Stack.d.ts +10 -10
- package/build/ts/surfaces/point-of-sale/components/Tabs/Tabs.doc.d.ts.map +1 -1
- package/build/ts/surfaces/point-of-sale/components/Text/Text.doc.d.ts.map +1 -1
- package/build/ts/surfaces/point-of-sale/components/TextField.d.ts +1 -1
- package/build/ts/surfaces/point-of-sale/components/Tile/Tile.doc.d.ts.map +1 -1
- package/build/ts/surfaces/point-of-sale/components/components-shared.d.ts +3249 -2916
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/surfaces/admin/api/action/examples/handle-errors.jsx +47 -0
- package/src/surfaces/admin/api/action/examples/process-selected-products.jsx +33 -0
- package/src/surfaces/admin/api/action/examples/select-additional-resources.jsx +46 -0
- package/src/surfaces/admin/api/block/examples/display-product-info.jsx +46 -0
- package/src/surfaces/admin/api/block/examples/navigate-to-action.jsx +50 -0
- package/src/surfaces/admin/api/block/examples/select-related-products.jsx +46 -0
- package/src/surfaces/admin/api/checkout-rules/examples/configure-shipping-restrictions.jsx +52 -0
- package/src/surfaces/admin/api/checkout-rules/examples/load-validation-config.jsx +55 -0
- package/src/surfaces/admin/api/checkout-rules/examples/set-minimum-quantity.jsx +40 -0
- package/src/surfaces/admin/api/customer-segment-template/examples/abandoned-cart-recovery.jsx +30 -0
- package/src/surfaces/admin/api/customer-segment-template/examples/birthday-this-month.jsx +32 -0
- package/src/surfaces/admin/api/customer-segment-template/examples/high-value-customers.jsx +27 -0
- package/src/surfaces/admin/api/discount-function-settings/examples/configure-discount-threshold.jsx +39 -0
- package/src/surfaces/admin/api/discount-function-settings/examples/configure-eligibility-rules.jsx +45 -0
- package/src/surfaces/admin/api/discount-function-settings/examples/load-existing-settings.jsx +45 -0
- package/src/surfaces/admin/api/intents/examples/create-article.jsx +35 -0
- package/src/surfaces/admin/api/intents/examples/create-catalog.jsx +35 -0
- package/src/surfaces/admin/api/intents/examples/create-collection.jsx +35 -0
- package/src/surfaces/admin/api/intents/examples/create-customer.jsx +35 -0
- package/src/surfaces/admin/api/intents/examples/create-discount.jsx +35 -0
- package/src/surfaces/admin/api/intents/examples/create-market.jsx +35 -0
- package/src/surfaces/admin/api/intents/examples/create-menu.jsx +35 -0
- package/src/surfaces/admin/api/intents/examples/create-metafield-definition.jsx +35 -0
- package/src/surfaces/admin/api/intents/examples/create-metaobject-definition.jsx +35 -0
- package/src/surfaces/admin/api/intents/examples/create-metaobject.jsx +35 -0
- package/src/surfaces/admin/api/intents/examples/create-page.jsx +35 -0
- package/src/surfaces/admin/api/intents/examples/create-product.jsx +37 -0
- package/src/surfaces/admin/api/intents/examples/create-variant.jsx +35 -0
- package/src/surfaces/admin/api/intents/examples/edit-article.jsx +41 -0
- package/src/surfaces/admin/api/intents/examples/edit-catalog.jsx +41 -0
- package/src/surfaces/admin/api/intents/examples/edit-collection.jsx +41 -0
- package/src/surfaces/admin/api/intents/examples/edit-customer.jsx +41 -0
- package/src/surfaces/admin/api/intents/examples/edit-discount.jsx +41 -0
- package/src/surfaces/admin/api/intents/examples/edit-market.jsx +41 -0
- package/src/surfaces/admin/api/intents/examples/edit-menu.jsx +41 -0
- package/src/surfaces/admin/api/intents/examples/edit-metafield-definition.jsx +41 -0
- package/src/surfaces/admin/api/intents/examples/edit-metaobject-definition.jsx +41 -0
- package/src/surfaces/admin/api/intents/examples/edit-metaobject.jsx +41 -0
- package/src/surfaces/admin/api/intents/examples/edit-page.jsx +41 -0
- package/src/surfaces/admin/api/intents/examples/edit-product.jsx +41 -0
- package/src/surfaces/admin/api/intents/examples/edit-variant.jsx +41 -0
- package/src/surfaces/admin/api/order-routing-rule/examples/configure-location-priority.jsx +46 -0
- package/src/surfaces/admin/api/order-routing-rule/examples/remove-deprecated-settings.jsx +35 -0
- package/src/surfaces/admin/api/order-routing-rule/examples/set-routing-criteria.jsx +57 -0
- package/src/surfaces/admin/api/picker/examples/direct-api.jsx +42 -0
- package/src/surfaces/admin/api/picker/examples/disabled.jsx +23 -0
- package/src/surfaces/admin/api/picker/examples/minimal.jsx +37 -0
- package/src/surfaces/admin/api/picker/examples/multiple-limit.jsx +26 -0
- package/src/surfaces/admin/api/picker/examples/multiple-true.jsx +25 -0
- package/src/surfaces/admin/api/picker/examples/preselected.jsx +23 -0
- package/src/surfaces/admin/api/picker/examples/template-picker.jsx +58 -0
- package/src/surfaces/admin/api/print-action/examples/custom-product-labels.jsx +44 -0
- package/src/surfaces/admin/api/print-action/examples/generate-packing-slip.jsx +28 -0
- package/src/surfaces/admin/api/print-action/examples/shipping-manifest.jsx +58 -0
- package/src/surfaces/admin/api/product-configuration/examples/create-variant-component.jsx +56 -0
- package/src/surfaces/admin/api/product-configuration/examples/load-bundle-config.jsx +47 -0
- package/src/surfaces/admin/api/product-configuration/examples/load-variant-bundle-config.jsx +54 -0
- package/src/surfaces/admin/api/product-configuration/examples/navigate-to-component.jsx +27 -0
- package/src/surfaces/admin/api/product-configuration/examples/select-bundle-components.jsx +50 -0
- package/src/surfaces/admin/api/product-configuration/examples/select-variant-components.jsx +50 -0
- package/src/surfaces/admin/api/purchase-options-card/examples/manage-subscription.jsx +45 -0
- package/src/surfaces/admin/api/purchase-options-card/examples/remove-from-plan.jsx +44 -0
- package/src/surfaces/admin/api/purchase-options-card/examples/validate-selling-plan.jsx +47 -0
- package/src/surfaces/admin/api/resource-picker/examples/action.jsx +25 -0
- package/src/surfaces/admin/api/resource-picker/examples/collection-picker.jsx +22 -0
- package/src/surfaces/admin/api/resource-picker/examples/filter-query.jsx +22 -0
- package/src/surfaces/admin/api/resource-picker/examples/filters.jsx +27 -0
- package/src/surfaces/admin/api/resource-picker/examples/multiple-limited.jsx +25 -0
- package/src/surfaces/admin/api/resource-picker/examples/multiple-unlimited.jsx +25 -0
- package/src/surfaces/admin/api/resource-picker/examples/product-picker.jsx +22 -0
- package/src/surfaces/admin/api/resource-picker/examples/product-variant-picker.jsx +22 -0
- package/src/surfaces/admin/api/resource-picker/examples/query.jsx +25 -0
- package/src/surfaces/admin/api/resource-picker/examples/selection-ids.jsx +28 -0
- package/src/surfaces/admin/api/resource-picker/examples/selection.jsx +22 -0
- package/src/surfaces/admin/api/should-render/examples/bulk-selection-check.jsx +8 -0
- package/src/surfaces/admin/api/should-render/examples/check-order-status.jsx +7 -0
- package/src/surfaces/admin/api/should-render/examples/check-product-tag.jsx +7 -0
- package/src/surfaces/admin/api/standard/examples/authenticate-backend-request.jsx +40 -0
- package/src/surfaces/admin/api/standard/examples/persist-settings.jsx +43 -0
- package/src/surfaces/admin/api/standard/examples/query-and-mutate.jsx +75 -0
- package/src/surfaces/admin/api/standard/storage.ts +1 -1
- package/src/surfaces/admin/extension-targets.ts +1 -1
- package/src/surfaces/checkout/api/address-autocomplete/standard.ts +5 -0
- package/src/surfaces/checkout/api/standard/standard.ts +4 -2
- package/src/surfaces/checkout/preact/metafield.ts +1 -0
- package/src/surfaces/checkout/preact/metafields.ts +1 -0
- package/src/surfaces/point-of-sale/components/EmptyState.d.ts +44 -34
- package/src/surfaces/point-of-sale/components/Stack.d.ts +10 -10
- package/src/surfaces/point-of-sale/components/TextField.d.ts +1 -1
- package/src/surfaces/point-of-sale/components/components-shared.d.ts +3249 -2916
- package/src/surfaces/point-of-sale/components.d.ts +6504 -5829
- package/src/surfaces/admin/api/intents/examples/create-article.js +0 -9
- package/src/surfaces/admin/api/intents/examples/create-catalog.js +0 -9
- package/src/surfaces/admin/api/intents/examples/create-collection.js +0 -9
- package/src/surfaces/admin/api/intents/examples/create-customer.js +0 -9
- package/src/surfaces/admin/api/intents/examples/create-discount.js +0 -11
- package/src/surfaces/admin/api/intents/examples/create-market.js +0 -9
- package/src/surfaces/admin/api/intents/examples/create-menu.js +0 -9
- package/src/surfaces/admin/api/intents/examples/create-metafield-definition.js +0 -11
- package/src/surfaces/admin/api/intents/examples/create-metaobject-definition.js +0 -9
- package/src/surfaces/admin/api/intents/examples/create-metaobject.js +0 -11
- package/src/surfaces/admin/api/intents/examples/create-page.js +0 -9
- package/src/surfaces/admin/api/intents/examples/create-product.js +0 -9
- package/src/surfaces/admin/api/intents/examples/create-variant.js +0 -11
- package/src/surfaces/admin/api/intents/examples/edit-article.js +0 -11
- package/src/surfaces/admin/api/intents/examples/edit-catalog.js +0 -11
- package/src/surfaces/admin/api/intents/examples/edit-collection.js +0 -11
- package/src/surfaces/admin/api/intents/examples/edit-customer.js +0 -11
- package/src/surfaces/admin/api/intents/examples/edit-discount.js +0 -11
- package/src/surfaces/admin/api/intents/examples/edit-market.js +0 -11
- package/src/surfaces/admin/api/intents/examples/edit-menu.js +0 -11
- package/src/surfaces/admin/api/intents/examples/edit-metafield-definition.js +0 -12
- package/src/surfaces/admin/api/intents/examples/edit-metaobject-definition.js +0 -11
- package/src/surfaces/admin/api/intents/examples/edit-metaobject.js +0 -12
- package/src/surfaces/admin/api/intents/examples/edit-page.js +0 -11
- package/src/surfaces/admin/api/intents/examples/edit-product.js +0 -11
- package/src/surfaces/admin/api/intents/examples/edit-variant.js +0 -12
- package/src/surfaces/admin/api/picker/examples/direct-api.js +0 -73
- package/src/surfaces/admin/api/picker/examples/disabled.js +0 -16
- package/src/surfaces/admin/api/picker/examples/minimal.js +0 -16
- package/src/surfaces/admin/api/picker/examples/multiple-limit.js +0 -21
- package/src/surfaces/admin/api/picker/examples/multiple-true.js +0 -21
- package/src/surfaces/admin/api/picker/examples/preselected.js +0 -16
- package/src/surfaces/admin/api/picker/examples/template-picker.js +0 -37
- package/src/surfaces/admin/api/resource-picker/examples/action.js +0 -4
- package/src/surfaces/admin/api/resource-picker/examples/collection-picker.js +0 -1
- package/src/surfaces/admin/api/resource-picker/examples/filter-query.js +0 -6
- package/src/surfaces/admin/api/resource-picker/examples/filters.js +0 -9
- package/src/surfaces/admin/api/resource-picker/examples/multiple-limited.js +0 -4
- package/src/surfaces/admin/api/resource-picker/examples/multiple-unlimited.js +0 -4
- package/src/surfaces/admin/api/resource-picker/examples/product-picker.js +0 -3
- package/src/surfaces/admin/api/resource-picker/examples/product-variant-picker.js +0 -1
- package/src/surfaces/admin/api/resource-picker/examples/query.js +0 -4
- package/src/surfaces/admin/api/resource-picker/examples/selection-ids.js +0 -16
- package/src/surfaces/admin/api/resource-picker/examples/selection.js +0 -7
package/package.json
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {render} from 'preact';
|
|
2
|
+
import {useState} from 'preact/hooks';
|
|
3
|
+
|
|
4
|
+
export default async () => {
|
|
5
|
+
render(<Extension />, document.body);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function Extension() {
|
|
9
|
+
const {data} = shopify;
|
|
10
|
+
const [error, setError] = useState(null);
|
|
11
|
+
const [loading, setLoading] = useState(false);
|
|
12
|
+
|
|
13
|
+
const handleFulfill = async () => {
|
|
14
|
+
setLoading(true);
|
|
15
|
+
setError(null);
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const orderId = data.selected[0]?.id;
|
|
19
|
+
|
|
20
|
+
const response = await fetch(`/api/orders/${orderId}/fulfill`, {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const result = await response.json();
|
|
25
|
+
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw new Error(result.error || 'Fulfillment failed');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.log('Order fulfilled:', result);
|
|
31
|
+
shopify.close();
|
|
32
|
+
} catch (err) {
|
|
33
|
+
setError(err.message);
|
|
34
|
+
} finally {
|
|
35
|
+
setLoading(false);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<s-admin-action title="Fulfill Order">
|
|
41
|
+
{error && <s-banner status="critical">{error}</s-banner>}
|
|
42
|
+
<s-button onClick={handleFulfill} disabled={loading}>
|
|
43
|
+
{loading ? 'Fulfilling...' : 'Fulfill Order'}
|
|
44
|
+
</s-button>
|
|
45
|
+
</s-admin-action>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {render} from 'preact';
|
|
2
|
+
|
|
3
|
+
export default async () => {
|
|
4
|
+
render(<Extension />, document.body);
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
function Extension() {
|
|
8
|
+
const {data} = shopify;
|
|
9
|
+
|
|
10
|
+
const handleProcess = async () => {
|
|
11
|
+
const productIds = data.selected.map((item) => item.id);
|
|
12
|
+
|
|
13
|
+
const response = await fetch('/api/bulk-process', {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
headers: {'Content-Type': 'application/json'},
|
|
16
|
+
body: JSON.stringify({productIds}),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
if (response.ok) {
|
|
20
|
+
console.log('Products processed successfully');
|
|
21
|
+
shopify.close();
|
|
22
|
+
} else {
|
|
23
|
+
console.error('Failed to process products');
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<s-admin-action title="Bulk Process">
|
|
29
|
+
<s-text>Processing {data.selected.length} products</s-text>
|
|
30
|
+
<s-button onClick={handleProcess}>Process Products</s-button>
|
|
31
|
+
</s-admin-action>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {render} from 'preact';
|
|
2
|
+
import {useState} from 'preact/hooks';
|
|
3
|
+
|
|
4
|
+
export default async () => {
|
|
5
|
+
render(<Extension />, document.body);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function Extension() {
|
|
9
|
+
const {data} = shopify;
|
|
10
|
+
const [selected, setSelected] = useState(null);
|
|
11
|
+
|
|
12
|
+
const currentProductId = data.selected[0]?.id;
|
|
13
|
+
|
|
14
|
+
const handleSelectProducts = async () => {
|
|
15
|
+
const selectedProducts = await shopify.resourcePicker({
|
|
16
|
+
type: 'product',
|
|
17
|
+
multiple: 5,
|
|
18
|
+
action: 'select',
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (selectedProducts) {
|
|
22
|
+
setSelected(selectedProducts);
|
|
23
|
+
|
|
24
|
+
await fetch('/api/create-bundle', {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: {'Content-Type': 'application/json'},
|
|
27
|
+
body: JSON.stringify({
|
|
28
|
+
mainProduct: currentProductId,
|
|
29
|
+
components: selectedProducts.map((p) => p.id),
|
|
30
|
+
}),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
shopify.close();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<s-admin-action title="Create Bundle">
|
|
39
|
+
<s-text>Main product: {currentProductId}</s-text>
|
|
40
|
+
<s-button onClick={handleSelectProducts}>
|
|
41
|
+
Select Component Products
|
|
42
|
+
</s-button>
|
|
43
|
+
{selected && <s-text>Selected {selected.length} products</s-text>}
|
|
44
|
+
</s-admin-action>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {render} from 'preact';
|
|
2
|
+
import {useState, useEffect} from 'preact/hooks';
|
|
3
|
+
|
|
4
|
+
export default async () => {
|
|
5
|
+
render(<Extension />, document.body);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function Extension() {
|
|
9
|
+
const {data} = shopify;
|
|
10
|
+
const [product, setProduct] = useState(null);
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const fetchProduct = async () => {
|
|
14
|
+
const productId = data.selected[0]?.id;
|
|
15
|
+
|
|
16
|
+
const {data: productData} = await shopify.query(
|
|
17
|
+
`query GetProduct($id: ID!) {
|
|
18
|
+
product(id: $id) {
|
|
19
|
+
title
|
|
20
|
+
totalInventory
|
|
21
|
+
status
|
|
22
|
+
}
|
|
23
|
+
}`,
|
|
24
|
+
{variables: {id: productId}},
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
setProduct(productData.product);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
fetchProduct();
|
|
31
|
+
}, [data]);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<s-admin-block heading="Product Information">
|
|
35
|
+
{product ? (
|
|
36
|
+
<>
|
|
37
|
+
<s-text>Title: {product.title}</s-text>
|
|
38
|
+
<s-text>Inventory: {product.totalInventory}</s-text>
|
|
39
|
+
<s-text>Status: {product.status}</s-text>
|
|
40
|
+
</>
|
|
41
|
+
) : (
|
|
42
|
+
<s-spinner />
|
|
43
|
+
)}
|
|
44
|
+
</s-admin-block>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {render} from 'preact';
|
|
2
|
+
import {useState, useEffect} from 'preact/hooks';
|
|
3
|
+
|
|
4
|
+
export default async () => {
|
|
5
|
+
render(<Extension />, document.body);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function Extension() {
|
|
9
|
+
const {data} = shopify;
|
|
10
|
+
const [eligible, setEligible] = useState(false);
|
|
11
|
+
const [checking, setChecking] = useState(true);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const checkEligibility = async () => {
|
|
15
|
+
const productId = data.selected[0]?.id;
|
|
16
|
+
|
|
17
|
+
if (!productId) {
|
|
18
|
+
setChecking(false);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const response = await fetch(`/api/products/${productId}/check-eligibility`, {
|
|
23
|
+
method: 'GET',
|
|
24
|
+
headers: {'Content-Type': 'application/json'},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const {eligible} = await response.json();
|
|
28
|
+
setEligible(eligible);
|
|
29
|
+
setChecking(false);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
checkEligibility();
|
|
33
|
+
}, [data]);
|
|
34
|
+
|
|
35
|
+
const handleNavigate = () => {
|
|
36
|
+
shopify.navigation.navigate('extension://my-product-action-extension-handle');
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<s-admin-block heading="Advanced Actions">
|
|
41
|
+
{checking ? (
|
|
42
|
+
<s-spinner />
|
|
43
|
+
) : eligible ? (
|
|
44
|
+
<s-button onClick={handleNavigate}>Launch Advanced Workflow</s-button>
|
|
45
|
+
) : (
|
|
46
|
+
<s-text>Product not eligible for advanced actions</s-text>
|
|
47
|
+
)}
|
|
48
|
+
</s-admin-block>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {render} from 'preact';
|
|
2
|
+
import {useState} from 'preact/hooks';
|
|
3
|
+
|
|
4
|
+
export default async () => {
|
|
5
|
+
render(<Extension />, document.body);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function Extension() {
|
|
9
|
+
const {data} = shopify;
|
|
10
|
+
const [relatedCount, setRelatedCount] = useState(0);
|
|
11
|
+
|
|
12
|
+
const currentProductId = data.selected[0]?.id;
|
|
13
|
+
|
|
14
|
+
const handleSelectRelated = async () => {
|
|
15
|
+
const selectedProducts = await shopify.resourcePicker({
|
|
16
|
+
type: 'product',
|
|
17
|
+
multiple: true,
|
|
18
|
+
filter: {
|
|
19
|
+
hidden: false,
|
|
20
|
+
draft: false,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (selectedProducts) {
|
|
25
|
+
await fetch('/api/product-recommendations', {
|
|
26
|
+
method: 'POST',
|
|
27
|
+
headers: {'Content-Type': 'application/json'},
|
|
28
|
+
body: JSON.stringify({
|
|
29
|
+
productId: currentProductId,
|
|
30
|
+
relatedProducts: selectedProducts.map((p) => p.id),
|
|
31
|
+
}),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
setRelatedCount(selectedProducts.length);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<s-admin-block heading="Product Recommendations">
|
|
40
|
+
<s-button onClick={handleSelectRelated}>Select Related Products</s-button>
|
|
41
|
+
{relatedCount > 0 && (
|
|
42
|
+
<s-text>Added {relatedCount} related products</s-text>
|
|
43
|
+
)}
|
|
44
|
+
</s-admin-block>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {render} from 'preact';
|
|
2
|
+
import {useState} from 'preact/hooks';
|
|
3
|
+
|
|
4
|
+
export default async () => {
|
|
5
|
+
render(<Extension />, document.body);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function Extension() {
|
|
9
|
+
const {data} = shopify;
|
|
10
|
+
const [countries, setCountries] = useState('US, CA, GB');
|
|
11
|
+
const [errorMsg, setErrorMsg] = useState('Shipping not available to your location');
|
|
12
|
+
|
|
13
|
+
const handleSave = async () => {
|
|
14
|
+
const blockedCountries = countries.split(',').map((c) => c.trim());
|
|
15
|
+
|
|
16
|
+
await shopify.applyMetafieldChange({
|
|
17
|
+
type: 'updateMetafield',
|
|
18
|
+
namespace: 'validation',
|
|
19
|
+
key: 'blocked_shipping_countries',
|
|
20
|
+
value: JSON.stringify(blockedCountries),
|
|
21
|
+
valueType: 'json',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
await shopify.applyMetafieldChange({
|
|
25
|
+
type: 'updateMetafield',
|
|
26
|
+
namespace: 'validation',
|
|
27
|
+
key: 'error_message',
|
|
28
|
+
value: errorMsg,
|
|
29
|
+
valueType: 'single_line_text_field',
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<s-function-settings>
|
|
35
|
+
<s-stack direction="block">
|
|
36
|
+
<s-text-field
|
|
37
|
+
label="Blocked countries (comma-separated)"
|
|
38
|
+
value={countries}
|
|
39
|
+
onChange={(value) => setCountries(value)}
|
|
40
|
+
/>
|
|
41
|
+
<s-text-field
|
|
42
|
+
label="Error message"
|
|
43
|
+
value={errorMsg}
|
|
44
|
+
onChange={(value) => setErrorMsg(value)}
|
|
45
|
+
/>
|
|
46
|
+
<s-button onClick={handleSave}>Save Restrictions</s-button>
|
|
47
|
+
<s-text>Validation ID: {data.validation?.id}</s-text>
|
|
48
|
+
<s-text>Function ID: {data.shopifyFunction.id}</s-text>
|
|
49
|
+
</s-stack>
|
|
50
|
+
</s-function-settings>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {render} from 'preact';
|
|
2
|
+
import {useState, useEffect} from 'preact/hooks';
|
|
3
|
+
|
|
4
|
+
export default async () => {
|
|
5
|
+
render(<Extension />, document.body);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function Extension() {
|
|
9
|
+
const {data} = shopify;
|
|
10
|
+
const [mode, setMode] = useState('loading');
|
|
11
|
+
const [settings, setSettings] = useState({});
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const initializeSettings = async () => {
|
|
15
|
+
if (data.validation) {
|
|
16
|
+
const config = data.validation.metafields.reduce((acc, field) => {
|
|
17
|
+
acc[field.key] = field.value;
|
|
18
|
+
return acc;
|
|
19
|
+
}, {});
|
|
20
|
+
|
|
21
|
+
setSettings(config);
|
|
22
|
+
setMode('edit');
|
|
23
|
+
} else {
|
|
24
|
+
await shopify.applyMetafieldChange({
|
|
25
|
+
type: 'updateMetafield',
|
|
26
|
+
namespace: 'validation',
|
|
27
|
+
key: 'default_rule',
|
|
28
|
+
value: 'require_minimum_cart_total',
|
|
29
|
+
valueType: 'single_line_text_field',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
setMode('created');
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
initializeSettings();
|
|
37
|
+
}, [data]);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<s-function-settings>
|
|
41
|
+
{mode === 'loading' && <s-spinner />}
|
|
42
|
+
{mode === 'edit' && (
|
|
43
|
+
<>
|
|
44
|
+
<s-text>Editing existing validation</s-text>
|
|
45
|
+
{Object.entries(settings).map(([key, value]) => (
|
|
46
|
+
<s-text key={key}>
|
|
47
|
+
{key}: {value}
|
|
48
|
+
</s-text>
|
|
49
|
+
))}
|
|
50
|
+
</>
|
|
51
|
+
)}
|
|
52
|
+
{mode === 'created' && <s-text>Created new validation configuration</s-text>}
|
|
53
|
+
</s-function-settings>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {render} from 'preact';
|
|
2
|
+
import {useState} from 'preact/hooks';
|
|
3
|
+
|
|
4
|
+
export default async () => {
|
|
5
|
+
render(<Extension />, document.body);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function Extension() {
|
|
9
|
+
const [quantity, setQuantity] = useState('3');
|
|
10
|
+
const [result, setResult] = useState(null);
|
|
11
|
+
|
|
12
|
+
const handleSave = async () => {
|
|
13
|
+
const res = await shopify.applyMetafieldChange({
|
|
14
|
+
type: 'updateMetafield',
|
|
15
|
+
namespace: 'validation',
|
|
16
|
+
key: 'minimum_quantity',
|
|
17
|
+
value: quantity,
|
|
18
|
+
valueType: 'number_integer',
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
setResult(res);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<s-function-settings>
|
|
26
|
+
<s-number-field
|
|
27
|
+
label="Minimum quantity"
|
|
28
|
+
value={quantity}
|
|
29
|
+
onChange={(value) => setQuantity(value)}
|
|
30
|
+
/>
|
|
31
|
+
<s-button onClick={handleSave}>Save Validation</s-button>
|
|
32
|
+
{result?.type === 'success' && (
|
|
33
|
+
<s-banner status="success">Minimum quantity configured</s-banner>
|
|
34
|
+
)}
|
|
35
|
+
{result?.type === 'error' && (
|
|
36
|
+
<s-banner status="critical">{result.message}</s-banner>
|
|
37
|
+
)}
|
|
38
|
+
</s-function-settings>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export default () => {
|
|
2
|
+
return {
|
|
3
|
+
templates: [
|
|
4
|
+
{
|
|
5
|
+
title: 'Cart abandoners',
|
|
6
|
+
description: [
|
|
7
|
+
'Customers who abandoned carts in the last 7 days',
|
|
8
|
+
'Use this segment for email recovery campaigns',
|
|
9
|
+
],
|
|
10
|
+
query: `{
|
|
11
|
+
abandoned_checkouts_count: {
|
|
12
|
+
min: 1
|
|
13
|
+
}
|
|
14
|
+
last_abandoned_order_date: {
|
|
15
|
+
min: "${new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString()}"
|
|
16
|
+
}
|
|
17
|
+
}`,
|
|
18
|
+
queryToInsert: `{
|
|
19
|
+
abandoned_checkouts_count: {
|
|
20
|
+
min: 1
|
|
21
|
+
}
|
|
22
|
+
last_abandoned_order_date: {
|
|
23
|
+
min: "LAST_7_DAYS"
|
|
24
|
+
}
|
|
25
|
+
}`,
|
|
26
|
+
createdOn: '2025-01-15T00:00:00Z',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export default () => {
|
|
2
|
+
return {
|
|
3
|
+
templates: [
|
|
4
|
+
{
|
|
5
|
+
title: 'Birthday this month',
|
|
6
|
+
description: 'Customers with birthdays in the current month',
|
|
7
|
+
query: `{
|
|
8
|
+
metafields: {
|
|
9
|
+
key: "birth_date"
|
|
10
|
+
namespace: "customer"
|
|
11
|
+
value: {
|
|
12
|
+
month: ${new Date().getMonth() + 1}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}`,
|
|
16
|
+
queryToInsert: `{
|
|
17
|
+
metafields: {
|
|
18
|
+
key: "birth_date"
|
|
19
|
+
namespace: "customer"
|
|
20
|
+
value: {
|
|
21
|
+
month: ${new Date().getMonth() + 1}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}`,
|
|
25
|
+
dependencies: {
|
|
26
|
+
standardMetafields: ['facts.birth_date'],
|
|
27
|
+
},
|
|
28
|
+
createdOn: '2025-01-15T00:00:00Z',
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export default () => {
|
|
2
|
+
return {
|
|
3
|
+
templates: [
|
|
4
|
+
{
|
|
5
|
+
title: shopify.i18n.translate('templates.highValue.title'),
|
|
6
|
+
description: shopify.i18n.translate('templates.highValue.description'),
|
|
7
|
+
query: `{
|
|
8
|
+
total_spent: {
|
|
9
|
+
min: 500
|
|
10
|
+
}
|
|
11
|
+
orders_count: {
|
|
12
|
+
min: 5
|
|
13
|
+
}
|
|
14
|
+
}`,
|
|
15
|
+
queryToInsert: `{
|
|
16
|
+
total_spent: {
|
|
17
|
+
min: 500
|
|
18
|
+
}
|
|
19
|
+
orders_count: {
|
|
20
|
+
min: 5
|
|
21
|
+
}
|
|
22
|
+
}`,
|
|
23
|
+
createdOn: '2025-01-15T00:00:00Z',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
};
|
package/src/surfaces/admin/api/discount-function-settings/examples/configure-discount-threshold.jsx
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {render} from 'preact';
|
|
2
|
+
import {useState} from 'preact/hooks';
|
|
3
|
+
|
|
4
|
+
export default async () => {
|
|
5
|
+
render(<Extension />, document.body);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function Extension() {
|
|
9
|
+
const [threshold, setThreshold] = useState('50.00');
|
|
10
|
+
const [saved, setSaved] = useState(false);
|
|
11
|
+
|
|
12
|
+
const handleSave = async () => {
|
|
13
|
+
const result = await shopify.applyMetafieldChange({
|
|
14
|
+
type: 'updateMetafield',
|
|
15
|
+
namespace: 'discount-config',
|
|
16
|
+
key: 'minimum_purchase',
|
|
17
|
+
value: threshold,
|
|
18
|
+
valueType: 'number_decimal',
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (result.type === 'success') {
|
|
22
|
+
setSaved(true);
|
|
23
|
+
} else {
|
|
24
|
+
console.error('Configuration failed:', result.message);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<s-function-settings>
|
|
30
|
+
<s-text-field
|
|
31
|
+
label="Minimum purchase amount"
|
|
32
|
+
value={threshold}
|
|
33
|
+
onChange={(value) => setThreshold(value)}
|
|
34
|
+
/>
|
|
35
|
+
<s-button onClick={handleSave}>Save Threshold</s-button>
|
|
36
|
+
{saved && <s-banner status="success">Threshold configured!</s-banner>}
|
|
37
|
+
</s-function-settings>
|
|
38
|
+
);
|
|
39
|
+
}
|
package/src/surfaces/admin/api/discount-function-settings/examples/configure-eligibility-rules.jsx
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {render} from 'preact';
|
|
2
|
+
import {useState} from 'preact/hooks';
|
|
3
|
+
|
|
4
|
+
export default async () => {
|
|
5
|
+
render(<Extension />, document.body);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function Extension() {
|
|
9
|
+
const [tags, setTags] = useState('vip, wholesale, premium');
|
|
10
|
+
const [maxUses, setMaxUses] = useState('5');
|
|
11
|
+
|
|
12
|
+
const handleSave = async () => {
|
|
13
|
+
await shopify.applyMetafieldChange({
|
|
14
|
+
type: 'updateMetafield',
|
|
15
|
+
namespace: 'discount-config',
|
|
16
|
+
key: 'eligible_customer_tags',
|
|
17
|
+
value: JSON.stringify(tags.split(',').map((t) => t.trim())),
|
|
18
|
+
valueType: 'json',
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
await shopify.applyMetafieldChange({
|
|
22
|
+
type: 'updateMetafield',
|
|
23
|
+
namespace: 'discount-config',
|
|
24
|
+
key: 'max_uses_per_customer',
|
|
25
|
+
value: maxUses,
|
|
26
|
+
valueType: 'number_integer',
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<s-function-settings>
|
|
32
|
+
<s-text-field
|
|
33
|
+
label="Eligible customer tags (comma-separated)"
|
|
34
|
+
value={tags}
|
|
35
|
+
onChange={(value) => setTags(value)}
|
|
36
|
+
/>
|
|
37
|
+
<s-number-field
|
|
38
|
+
label="Max uses per customer"
|
|
39
|
+
value={maxUses}
|
|
40
|
+
onChange={(value) => setMaxUses(value)}
|
|
41
|
+
/>
|
|
42
|
+
<s-button onClick={handleSave}>Save Eligibility Rules</s-button>
|
|
43
|
+
</s-function-settings>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {render} from 'preact';
|
|
2
|
+
import {useState, useEffect} from 'preact/hooks';
|
|
3
|
+
|
|
4
|
+
export default async () => {
|
|
5
|
+
render(<Extension />, document.body);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function Extension() {
|
|
9
|
+
const {data} = shopify;
|
|
10
|
+
const [settings, setSettings] = useState({});
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const initializeSettings = async () => {
|
|
14
|
+
const existingSettings = data.metafields.reduce((acc, field) => {
|
|
15
|
+
acc[field.key] = field.value;
|
|
16
|
+
return acc;
|
|
17
|
+
}, {});
|
|
18
|
+
|
|
19
|
+
setSettings(existingSettings);
|
|
20
|
+
|
|
21
|
+
if (!existingSettings.eligible_tags) {
|
|
22
|
+
await shopify.applyMetafieldChange({
|
|
23
|
+
type: 'updateMetafield',
|
|
24
|
+
namespace: 'discount-config',
|
|
25
|
+
key: 'eligible_tags',
|
|
26
|
+
value: JSON.stringify(['vip', 'wholesale']),
|
|
27
|
+
valueType: 'json',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
initializeSettings();
|
|
33
|
+
}, [data]);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<s-function-settings>
|
|
37
|
+
<s-text>Current settings:</s-text>
|
|
38
|
+
{Object.entries(settings).map(([key, value]) => (
|
|
39
|
+
<s-text key={key}>
|
|
40
|
+
{key}: {String(value)}
|
|
41
|
+
</s-text>
|
|
42
|
+
))}
|
|
43
|
+
</s-function-settings>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {render} from 'preact';
|
|
2
|
+
import {useState} from 'preact/hooks';
|
|
3
|
+
|
|
4
|
+
export default async () => {
|
|
5
|
+
render(<Extension />, document.body);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function Extension() {
|
|
9
|
+
const [result, setResult] = useState(null);
|
|
10
|
+
const [creating, setCreating] = useState(false);
|
|
11
|
+
|
|
12
|
+
const handleCreate = async () => {
|
|
13
|
+
setCreating(true);
|
|
14
|
+
|
|
15
|
+
const activity = await shopify.intents.invoke('create:shopify/Article');
|
|
16
|
+
const response = await activity.complete;
|
|
17
|
+
|
|
18
|
+
setResult(response);
|
|
19
|
+
setCreating(false);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<s-admin-block heading="Create Article">
|
|
24
|
+
<s-button onClick={handleCreate} disabled={creating}>
|
|
25
|
+
{creating ? 'Creating...' : 'Launch Article Creator'}
|
|
26
|
+
</s-button>
|
|
27
|
+
{result?.code === 'ok' && (
|
|
28
|
+
<s-banner status="success">Article created successfully!</s-banner>
|
|
29
|
+
)}
|
|
30
|
+
{result?.code === 'closed' && (
|
|
31
|
+
<s-text>Creation cancelled</s-text>
|
|
32
|
+
)}
|
|
33
|
+
</s-admin-block>
|
|
34
|
+
);
|
|
35
|
+
}
|