Displaying the Bundles widget on the Affinity overview page
Drive build-a-box customers to the portal with unique opportunities on the Affinity overview page
Businesses specializing in customizable bundles (ie. build-a-box), want to enable customers to seamlessly update their bundle content. This functionality often represents the primary reason customers visit the customer portal.
This guide outlines the steps to integrate and display the Bundles widget in Affinity's header slot, ensuring a seamless and user-friendly interface for customers managing their subscriptions.
Platform:
- Shopify Checkout Intergration
- Recharge Checkout on Shopify
How it works
Customers can update their bundled content within the subscription details view by default.
You may want to consider transferring this information to Affinity's entry page to improve the customer experience and engagement.
The widget for managing bundle subscriptions is hosted on a CDN. Embedding it is straightforward, requiring only the inclusion of the CDN link and the configuration of the global object. Affinity offers specific extension points (slots) for integrating this interface.
Note:
You must be on the Recharge Pro plan, or a Custom plan, to make advanced customizations to the Affinity customer portal.
Integrating the bundles widget into Affinity's header slot
Step 1: Slot preparation
- Slot selection: Use the
overview.header
to display the widget. - Target element: Create an HTML element with the unique ID
affinity-bundle-widget
within the slot content. Use this element to mount the app.
Step 2: App Initializion
- DOM tracking: Listen for the
Recharge::slot::mounted
to confirm the slot content was added to the DOM.
Step 3: Fetch Bundle Subscriptions
- SDK usage: Use the SDK to fetch active customer subscriptions. Include
bundle_product
in your query to filter. - Subscription identification: Locate the first subscription that meets the criteria for a customizable bundle.
Step 4: Widget mounting
- Context setting: Assign the
subscriptionID
towindow.BoxConfig.subscriptionConfig
to set the necessary context for the Bundles widget. - Widget loading: Trigger the
Recharge::bundling-widget::reload
event to initiate the widget.
Step 5: Styling
- Style consistency: Leverage Affinity's global CSS variables to ensure your styles aligns with your app's appearance.
- CSS inclusion: Incorporate the Bundles' widget CSS from the Recharge CDN.
Example
<link
rel="stylesheet"
href="https://static.rechargecdn.com/assets/bundling-widget/src.css"
referrerpolicy="origin">
<script src="https://static.rechargecdn.com/assets/bundling-widget/src.js"></script>
<style>
.aff-card {
background: var(--recharge-cards-background);
border: 1px solid var(--recharge-cards-border-color);
border-radius: var(--recharge-corners-radius);
padding: 20px;
line-height: 1.4;
}
.aff-button {
background-color: var(--recharge-button-brand);
border: 2px solid var(--recharge-button-brand);
border-radius: var(--recharge-button-border-radius);
color: var(--recharge-button-color) !important;
font-size: var(--recharge-typography-size-5);
font-weight: 600;
line-height: 150%;
padding: 10px 16px;
text-align: center;
cursor: pointer;
display: inline-block;
}
.aff-button[disabled=true] {
opacity: 0.6;
}
.aff-h3 {
font-size: var(--recharge-typography-size-3);
font-weight: bold;
line-height: 123%;
}
.t-mb2 {
margin-bottom: 8px;
}
.t-mb3 {
margin-bottom: 16px;
}
</style>
{% comment %} Slots {% endcomment %}
<script type="text/html" data-recharge-slot="overview.header">
<div id="affinity-bundle-section" class="aff-card t-mb3" style="display:none;">
<span class="aff-h3 t-mb2">Your box contents</span>
<div id="affinity-bundle-widget" class="affinity-bundle-widget"></div>
</div>
</script>
<script>
const rechargeAPI = {
session: null,
async authenticate() {
if (!this.session) {
this.session = await recharge.auth.loginCustomerPortal().catch(error => {
console.error("Authentication failed:", error);
throw error;
});
}
return this.session;
},
async fetchSubscriptions() {
const response = await recharge.subscription.listSubscriptions(this.session, {
limit: 10,
sort_by: 'id-asc',
status: 'Active',
include: 'bundle_product'
}).catch(error => {
console.error("Fetching subscriptions failed:", error);
throw error;
});
return response.subscriptions;
}
};
const bundlesWidgetManager = {
wrapperContainerId: 'affinity-bundle-section',
targetContainerId: 'affinity-bundle-widget',
async getBundleSubscription() {
const subscriptions = await rechargeAPI.fetchSubscriptions();
return subscriptions.find(sub => sub.include.bundle_product);
},
mountWidget(subscriptionId) {
window.BoxConfig = {
containerId: this.targetContainerId,
subscriptionConfig: { subscriptionId }
};
stateManager.refreshBundlesWidget();
this.handleBundleUpdate();
this.showWrapperSection();
},
showWrapperSection() {
document.getElementById(this.wrapperContainerId).style.display = 'block';
},
handleBundleUpdate() {
document.addEventListener(stateManager.widgetEventsToHandle.subscriptionUpdated, () => {
stateManager.refreshBundlesWidget();
});
},
async init() {
try {
await rechargeAPI.authenticate();
const bundleSubscription = await this.getBundleSubscription();
if (bundleSubscription) this.mountWidget(bundleSubscription.id);
} catch (error) {
console.error("Initialization failed:", error);
}
}
};
const stateManager = {
widgetEventsToHandle: {
subscriptionUpdated: "subscription:update"
},
widgetEventsToTrigger: {
refresh: "Recharge::bundling-widget::reload"
},
dispatchEvent(eventName) {
document.dispatchEvent(new CustomEvent(eventName));
},
refreshBundlesWidget() {
this.dispatchEvent(this.widgetEventsToTrigger.refresh);
}
};
document.addEventListener("Recharge::slot::mounted", event => {
if (event.detail.name === "header" && event.detail.pathname === "/overview") {
bundlesWidgetManager.init();
}
});
</script>
Updated 6 months ago