Skip to content

Commit 2340923

Browse files
committed
Add code examples of POS subscriptions UI extension
1 parent 78318cc commit 2340923

File tree

6 files changed

+158
-1
lines changed

6 files changed

+158
-1
lines changed

packages/ui-extensions/docs/surfaces/point-of-sale/mdxExamples/print-example/shopify.extension.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,3 @@ target = "pos.home.tile.render"
1313
[[extensions.targeting]]
1414
module = "./src/HomeModal.tsx"
1515
target = "pos.home.modal.render"
16-

packages/ui-extensions/docs/surfaces/point-of-sale/mdxExamples/subscription-example/Action.jsx

Whitespace-only changes.

packages/ui-extensions/docs/surfaces/point-of-sale/mdxExamples/subscription-example/MenuItem.jsx

Whitespace-only changes.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import {render} from 'preact';
2+
import {useState, useEffect} from 'preact/hooks';
3+
4+
// [START modal.fetch]
5+
async function fetchSellingPlans(variantId) {
6+
const requestBody = {
7+
query: `#graphql
8+
query GetSellingPlans($variantId: ID!) {
9+
productVariant(id: $variantId) {
10+
sellingPlanGroups(first: 10) {
11+
nodes {
12+
name
13+
sellingPlans(first: 10) {
14+
nodes {
15+
id
16+
name
17+
category
18+
}
19+
}
20+
}
21+
}
22+
}
23+
}
24+
`,
25+
variables: {variantId: `gid://shopify/ProductVariant/${variantId}`},
26+
};
27+
28+
const res = await fetch('shopify:admin/api/graphql.json', {
29+
method: 'POST',
30+
body: JSON.stringify(requestBody),
31+
});
32+
return res.json();
33+
}
34+
// [END modal.fetch]
35+
36+
export default function extension() {
37+
render(<Modal />, document.body);
38+
}
39+
40+
function Modal() {
41+
// For this example, we'll just use the first selling plan item
42+
const sellingPlanItem = shopify.cart.current.value.lineItems.find(
43+
(lineItem) => lineItem.hasSellingPlanGroups === true,
44+
);
45+
46+
const [response, setResponse] = useState(undefined);
47+
48+
useEffect(() => {
49+
async function getSellingPlans() {
50+
setResponse(await fetchSellingPlans(sellingPlanItem?.variantId));
51+
}
52+
getSellingPlans();
53+
}, [sellingPlanItem]);
54+
55+
// [START modal.handle-click]
56+
const handleClick = (plan) => {
57+
shopify.cart.addLineItemSellingPlan({
58+
lineItemUuid: sellingPlanItem.uuid,
59+
// convert from GID to ID
60+
sellingPlanId: Number(plan.id.split('/').pop()),
61+
sellingPlanName: plan.name,
62+
});
63+
window.close();
64+
};
65+
// [END modal.handle-click]
66+
67+
// [START modal.display]
68+
return (
69+
<s-page heading="POS modal">
70+
<s-scroll-box>
71+
<s-box padding="small">
72+
{response?.data.productVariant.sellingPlanGroups.nodes.map(
73+
(group) => {
74+
return (
75+
<s-section key={`${group.name}-section`} heading={group.name}>
76+
{group.sellingPlans.nodes.map((plan) => {
77+
return (
78+
<s-clickable
79+
key={`${plan.name}-clickable`}
80+
onClick={() => {
81+
handleClick(plan);
82+
}}
83+
>
84+
<s-text key={`${plan.name}-text`}>{plan.name}</s-text>
85+
</s-clickable>
86+
);
87+
})}
88+
</s-section>
89+
);
90+
},
91+
)}
92+
</s-box>
93+
</s-scroll-box>
94+
</s-page>
95+
);
96+
// [END modal.display]
97+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import {render} from 'preact';
2+
import {useState} from 'preact/hooks';
3+
4+
export default function extension() {
5+
render(<Tile />, document.body);
6+
}
7+
8+
function Tile() {
9+
const [sellingPlanEligible, setSellingPlanEligible] = useState(false);
10+
11+
// [START tile.subscribe]
12+
useEffect(() => {
13+
const unsubscribe = shopify.cart.current.subscribe(cart => {
14+
const sellingPlanEligibleLineItems = cart.lineItems.filter(lineItem => lineItem.hasSellingPlanGroups === true)
15+
16+
setSellingPlanEligible(sellingPlanEligibleLineItems.length > 0)
17+
})
18+
return unsubscribe
19+
}, [])
20+
// [END tile.subscribe]
21+
22+
return (
23+
<s-tile
24+
heading={"Subscriptions"}
25+
subheading={sellingPlanEligible
26+
? "Subscriptions available"
27+
: "Subscriptions not available"
28+
}
29+
// [START tile.disabled]
30+
disabled={!sellingPlanEligible}
31+
// [END tile.disabled]
32+
onClick={() => shopify.action.presentModal()}
33+
/>
34+
);
35+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
api_version = "2025-10"
2+
3+
[[extensions]]
4+
type = "ui_extension"
5+
name = "Subscription Tutorial"
6+
handle = "subscription-tutorial"
7+
description = "POS UI extension subscription tutorial"
8+
9+
[[extensions.targeting]]
10+
module = "./src/Tile.jsx"
11+
target = "pos.home.tile.render"
12+
13+
[[extensions.targeting]]
14+
module = "./src/Modal.jsx"
15+
target = "pos.home.modal.render"
16+
17+
# [START toml.optional_targets]
18+
# Optional additional targets for line item details screen
19+
[[extensions.targeting]]
20+
module = "./src/Action.tsx"
21+
target = "pos.cart.line-item-details.action.render"
22+
23+
[[extensions.targeting]]
24+
module = "./src/MenuItem.tsx"
25+
target = "pos.cart.line-item-details.action.menu-item.render"
26+
# [END toml.optional_targets]

0 commit comments

Comments
 (0)