avada-ui-bridge
v1.0.9
Published
Shared Cross App Banner components and hooks for Avada Shopify apps
Downloads
593
Readme
Avada UI Bridge
Shared components, hooks, and utilities for Avada Shopify apps.
Installation
npm install avada-ui-bridgePeer Dependencies
@shopify/polaris >= 12.0.0@shopify/react-i18n >= 7.0.0react >= 16.8.0react-dom >= 16.8.0react-router-dom
Components
AgeVerificationBanner
Standalone age verification banner that fetches shop data and manages its own state via API.
import { AgeVerificationBanner } from "avada-ui-bridge";
function MyComponent() {
return (
<AgeVerificationBanner
sourcePage="dashboard"
fetchAuthenticatedApi={(uri, options) => fetch(uri, options)}
/>
);
}Props
| Prop | Type | Required | Default | Description |
| ----------------------- | ------------------------------------ | -------- | ------------- | -------------------------------- |
| sourcePage | string | No | "dashboard" | Page where banner is displayed |
| fetchAuthenticatedApi | (uri: string, options: any) => any | Yes | - | Authenticated API fetch function |
BundleSaving
Displays a bundle saving promotion card that shows discount tiers based on how many Avada apps the merchant has installed. Fetches campaign data automatically from the Avada CRM API.
import { BundleSaving } from "avada-ui-bridge";
import "avada-ui-bridge/dist/styles.css";
<BundleSaving
plans={plans}
getMonthlyPricing={getMonthlyPricing}
onSubscribe={(planId, options) => {
store.dispatch(subscribeShopify(planId, options));
}}
/>;Props
| Prop | Type | Required | Default | Description |
| ------------------- | ----------------------------------------------------- | -------- | ------------------- | ------------------------------------------------------------ |
| shop | BundleSavingShop | No | window.activeShop | Shop data. Falls back to window.activeShop if not provided |
| plans | PlanConfig[] | Yes | - | Available plan configurations |
| getMonthlyPricing | (params: { shop, plan, interval }) => string | Yes | - | Function to calculate monthly pricing |
| freePlanId | string | No | "free" | ID of the free plan |
| subscriptionUrl | string | No | "/subscription" | URL for subscription management |
| onSubscribe | (planId: string, options: SubscribeOptions) => void | Yes | - | Callback when user subscribes with bundle discount |
BundleSavingShop
interface BundleSavingShop {
shopifyDomain: string;
appName: string; // Used to detect current app ("accessibility", "cookie", "pdf"/"invoice")
plan: string;
subStartAt?: string;
}SubscribeOptions
interface SubscribeOptions {
interval: string;
redirectUrl: string;
discountCode?: string;
}The component automatically detects the current app from shop.appName and shows other Avada apps (Accessibility, Cookie Bar, PDF Invoice) as bundle options. Discount tiers increase as more apps are installed. Navigation is handled internally via react-router-dom.
Hooks
useDisplayBanner
Manages banner visibility with localStorage persistence and shop-level dismissal tracking.
import {
useDisplayBanner,
TARGET_STORAGE_NEXT_DAY,
} from "avada-ui-bridge";
function MyBanner() {
const { displayBanner, closeBanner } = useDisplayBanner({
storageKey: "my-banner",
shopKey: "my-banner-key",
activeShop: shop,
checkShop: true,
disableCount: 3,
targetDateType: TARGET_STORAGE_NEXT_DAY,
checkStorage: true,
onClose: () => console.log("Banner closed"),
onDismiss: async (dismissedBanners) => {
await updateShop({ dismissedBanners });
},
});
if (!displayBanner) return null;
return (
<div>
My Banner Content
<button onClick={() => closeBanner()}>Close</button>
<button onClick={() => closeBanner(true)}>Don't show again</button>
</div>
);
}Options
| Option | Type | Required | Default | Description |
| ---------------- | ----------------------------------------------- | -------- | ------------------------ | -------------------------------------------------- |
| storageKey | string | Yes | - | LocalStorage key for persisting banner state |
| shopKey | string | No | '' | Shop identifier for dismissal tracking |
| activeShop | Shop | No | {} | Shop object with optional dismissedBanners array |
| checkShop | boolean | No | false | Whether to check shop-level dismissal |
| disableCount | number | No | 3 | Number of closures before permanently dismissing |
| targetDateType | TargetStorageType | No | TARGET_STORAGE_NEXT_DAY| When to re-show the banner after closing |
| checkStorage | boolean | No | true | Whether to check localStorage |
| onClose | () => void | No | () => {} | Callback when banner is closed |
| onDismiss | (dismissedBanners: string[]) => Promise<void> | No | - | Async callback to persist dismissal status |
Return
| Property | Type | Description |
| --------------- | ------------------------------------ | ---------------------------------------------------- |
| displayBanner | boolean | Whether the banner should be visible |
| closeBanner | (isDismissShop?: boolean) => void | Close the banner. Pass true to permanently dismiss |
useBundleCampaign
Fetches bundle campaign data from the Avada CRM API. Used internally by BundleSaving but can be used standalone.
import { useBundleCampaign } from "avada-ui-bridge";
function MyComponent() {
const { data, loading } = useBundleCampaign({
shopifyDomain: "mystore.myshopify.com",
appId: "cookieBar", // "cookieBar" | "seaAccessibility" | "pdfInvoice"
});
if (loading || !data) return null;
return (
<div>
<h2>{data.copy.title}</h2>
<p>Installed: {data.installedCount} / {data.totalApps}</p>
{data.applicableDiscount && (
<p>Discount: {data.applicableDiscount.percentOff}% off</p>
)}
</div>
);
}Return
interface UseBundleCampaignResult {
data: BundleCampaignData | null;
loading: boolean;
}
interface BundleCampaignData {
copy: { title: string; subtitle: string; compareLabel: string; footerNote: string };
discountRules: { minInstalled: number; percentOff: number }[];
applicableDiscount: { percentOff: number; couponCode?: string } | null;
installedCount: number;
totalApps: number;
apps: BundleApp[];
}Utility Functions
CrossApp Config
import {
getAgeVerification,
getAccessibilityApp,
getCookieBarApp,
getAppData,
} from "avada-ui-bridge";
const ageVerificationConfig = getAgeVerification(shop, "dashboard");
const accessibilityConfig = getAccessibilityApp(shop);
const cookieBarConfig = getCookieBarApp(shop);
// Generic function
const appConfig = getAppData("ageVerification", shop, "dashboard");Each function returns a CrossAppData object:
interface CrossAppData {
appHandle: string;
translationKey: string;
targetUrl: string;
showPlanBtn?: boolean;
planUrl?: string;
bgColor?: string;
isHideBanner?: boolean;
eventPrefix?: string;
bannerCloseKey?: string;
sourcePage?: string;
}Storage Utilities
import {
getStorageData,
setStorageData,
removeStorageData,
isEmpty,
} from "avada-ui-bridge";
setStorageData("my-key", { count: 1, date: new Date() });
const data = getStorageData("my-key", { count: 0 });
removeStorageData("my-key");
isEmpty(data); // true if null, empty array, or empty objectHelpers
import { getShopifyName } from "avada-ui-bridge";
getShopifyName("mystore.myshopify.com"); // "mystore"Constants
Grouped Constants
import {
BANNER_SOURCE_PAGES,
BANNER_ACTION_TYPES,
BANNER_SOURCE_APP,
BANNER_APP_HANDLE,
BANNER_APP_DOMAIN,
BANNER_SHOPIFY_APP_HANDLE,
BANNER_PLAN,
} from "avada-ui-bridge";| Constant | Keys | Values |
| ------------------------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BANNER_SOURCE_PAGES | DASHBOARD, SETTINGS, ORDERS, PRODUCTS, SCANNER, CONSENT_LOGS, BANNER_SETTINGS | "dashboard", "settings", "orders", "products", "scanner", "consentlogs", "bannersettings" |
| BANNER_ACTION_TYPES | DISPLAY, ACCESS, CLOSE | "display", "access", "close" |
| BANNER_SOURCE_APP | COOKIE_BAR, ORDER_LIMIT | "cb", "ol" |
| BANNER_APP_HANDLE | COOKIE_BAR, ACCESSIBILITY, AGE_VERIFICATION | "cookieBar", "accessibility", "ageVerification" |
| BANNER_APP_DOMAIN | COOKIE_BAR, ACCESSIBILITY, AGE_VERIFICATION | "cookie.avada.io", "sea-accessibility.firebaseapp.com", "age-verification-b0fa4.firebaseapp.com" |
| BANNER_SHOPIFY_APP_HANDLE | COOKIE_BAR, ACCESSIBILITY, AGE_VERIFICATION | "avada-cookie-bar", "sea-accessibility-ada-wcag", "sun-age-verification-popup" |
| BANNER_PLAN | ENTERPRISE | "enterprise" |
Storage Target Date Types
import {
TARGET_STORAGE_NEXT_DAY,
TARGET_STORAGE_NEXT_WEEK,
TARGET_STORAGE_NEXT_MONDAY,
TARGET_STORAGE_NEXT_MONDAY_AND_THURSDAY,
TARGET_STORAGE_7_DAYS_AFTER,
} from "avada-ui-bridge";Bundle Saving I18N Keys
import { BUNDLE_SAVING_I18N_KEYS } from "avada-ui-bridge";Exports all required i18n translation keys for the BundleSaving component. Use this to ensure your app provides all necessary translations.
Backward Compatibility Aliases
import {
AGE_VERIFICATION_HANDLE, // = BANNER_APP_HANDLE.AGE_VERIFICATION
ACCESSIBILITY_HANDLE, // = BANNER_APP_HANDLE.ACCESSIBILITY
COOKIE_BAR_HANDLE, // = BANNER_APP_HANDLE.COOKIE_BAR
AGE_VERIFICATION_DOMAIN, // = BANNER_APP_DOMAIN.AGE_VERIFICATION
ACCESSIBILITY_DOMAIN, // = BANNER_APP_DOMAIN.ACCESSIBILITY
COOKIE_BAR_DOMAIN, // = BANNER_APP_DOMAIN.COOKIE_BAR
AGE_VERIFICATION_APP_HANDLE, // = BANNER_SHOPIFY_APP_HANDLE.AGE_VERIFICATION
ACCESSIBILITY_APP_HANDLE, // = BANNER_SHOPIFY_APP_HANDLE.ACCESSIBILITY
COOKIE_BAR_APP_HANDLE, // = BANNER_SHOPIFY_APP_HANDLE.COOKIE_BAR
ENTERPRISE, // = BANNER_PLAN.ENTERPRISE
} from "avada-ui-bridge";TypeScript Types
import type {
// AgeVerificationBanner
AgeVerificationBannerProps,
// useDisplayBanner
UseDisplayBannerOptions,
UseDisplayBannerResult,
Shop,
StorageData,
TargetStorageType,
// BundleSaving
BundleSavingProps,
BundleSavingShop,
PlanConfig,
SubscribeOptions,
// CrossApp Config
CrossAppData,
ShopData,
// Constants
BannerActionType,
BannerSourcePage,
BannerSourceApp,
BannerAppHandle,
BannerAppDomain,
BannerShopifyAppHandle,
BannerPlan,
} from "avada-ui-bridge";Translations
BundleSaving Component
Use BUNDLE_SAVING_I18N_KEYS to get the full list of required keys. Key categories:
OtherCompliance.bundle.copy.*- Header copyOtherCompliance.bundle.progressBar.*- Progress bar labelsOtherCompliance.bundle.priceCards.*- Pricing card labelsOtherCompliance.bundle.appCards.*- App card action labelsOtherCompliance.cards.accessibility.*- Accessibility app card contentOtherCompliance.cards.dataProtection.*- Cookie Bar app card contentOtherCompliance.cards.eInvoice.*- PDF Invoice app card content
BundleSaving en.json
{
"OtherCompliance": {
"cards": {
"eInvoice": {
"title": "E-Invoice",
"badge": "Required for EU, US, LATAM & APAC",
"features": {
"compliance": "Ensures legal invoicing compliance across global markets",
"tax": "Simplifies tax reporting and accounting",
"transparency": "Boosts transparency and customer trust",
"automation": "Automates order invoicing (send, print, download)",
"manualWork": "Reduces manual work and risk of legal penalties"
},
"appName": "PDF Invoice",
"buttonText": "Install",
"freePlan": "Free plan available"
},
"accessibility": {
"title": "Accessibility (ADA, EAA, WCAG)",
"badge": "Required for EU & US",
"features": {
"accessible": "Ensures that digital content is accessible to all customers",
"experience": "Enhances user experience",
"engagement": "Boosts customer engagement",
"performance": "Improves your store's performance",
"legal": "Reduces the risk of legal action and penalties"
},
"appName": "SEA Accessibility",
"buttonText": "Install",
"freePlan": "Free plan available"
},
"dataProtection": {
"title": "Data protection (GDPR, CCPA)",
"badge": "Required for EU & US",
"features": {
"privacy": "Protects personal data and privacy of customers",
"trust": "Enhances customer trust",
"engagement": "Boosts customer engagement",
"reputation": "Protects brand reputation",
"legal": "Reduces the risk of legal action and penalties"
},
"appName": "Avada GDPR Cookies Consent"
}
},
"bundle": {
"priceCards": {
"currentPlan": "Current plan",
"bestChoice": "Best choice",
"perMonth": "/month",
"downgradeTitle": "Downgrade to Free plan",
"confirmDowngrade": "Confirm downgrade",
"cancel": "Cancel",
"downgradeMessage": "Are you sure you want to downgrade to the Free plan? You will lose access to premium features.",
"planNames": {
"cookieBar": {
"free": "Free",
"pro": "Professional",
"advanced": "Advanced",
"enterprise": "Enterprise"
},
"seaAccessibility": {
"free": "Free",
"starter": "Starter",
"growth": "Growth",
"scale": "Scale"
},
"pdfInvoice": {
"free": "Free",
"professional": "Professional",
"ultimate": "Ultimate",
"wholesale": "Wholesale"
}
}
},
"progressBar": {
"awayFromDiscount": "You are {appsNeeded} app away from getting {percentOff}% off 🔥",
"maxDiscount": "Amazing! You're saving {currentPercent}% — maximum discount reached!",
"addOneMore": "Amazing! You're saving {currentPercent}% — add 1 more for max {percentOff}% off!",
"savingGreat": "Great! You're saving {currentPercent}% — add {appsNeeded} more to save {percentOff}%",
"savingAmazing": "Amazing! You're saving {currentPercent}% — add {appsNeeded} more for max {percentOff}% off!",
"percentOff": "{percentOff}% off",
"appInstalled": "{count} app installed",
"appsInstalled": "{count} apps installed"
},
"appCards": {
"subscribed": "Subscribed",
"subscribe": "Subscribe",
"install": "Install",
"freePlanAvailable": "Free plan available"
},
"copy": {
"title": "🎉 Stack your savings — the more apps you use, the more you save!",
"subtitle": "Prices below reflect your bundle discount. Final price confirmed at checkout",
"compareLabel": "Your discounted prices - active now!",
"footerNote": "Discount applies automatically at checkout"
}
}
}
}License
MIT
