# AppManager Web Components SDK

Lightweight, framework-agnostic pricing UI SDK with no external UI dependencies.

### Build
```
npm install
npm run build
```

### Demo
- Run `npm run dev` and open `/public/serve.html`.
- Edit source files for instant live reload.

### Usage

Include the SDK via CDN in your HTML:

```html
<script src="https://cdn.jsdelivr.net/npm/@shop-circle/app-manager-web-components@{version}/dist/app-manager-web-components.umd.js"></script>
```

#### Components

- `<app-manager-billing-page>`: Use this for custom UI (no external dependencies required).
- `<app-manager-billing-page-polaris>`: Use this if your app uses Shopify Polaris web components.
  - **Important:** You must also include the Polaris script in your HTML if it is not already present:
    ```html
    <script src="https://cdn.shopify.com/shopifycloud/polaris.js"></script>
    ```

### Listening for Plan Selection Events

Both `<app-manager-billing-page>` and `<app-manager-billing-page-polaris>` emit a custom event `app-manager:plan-select` when a user selects either the "Free plan" or chooses to "Choose later". You can listen for this event and handle each case separately:

```js
document.addEventListener('app-manager:plan-select', e => {
  if (e.detail && e.detail.free_plan) {
    console.log('Free plan selected:', e.detail);
    // Handle free plan selection
  } else if (e.detail && e.detail.choose_later) {
    console.log('Choose later selected:', e.detail);
    // Handle choose later action
  }
});
```

### Attributes

Both `<app-manager-billing-page>` and `<app-manager-billing-page-polaris>` share the same attributes.

#### Required

| Attribute | Description |
|---|---|
| `base-url` | Base URL of your AppManager API |
| `shop-domain` | Myshopify domain of the store |
| `host` | Shopify host parameter |

#### Optional

| Attribute | Values | Default | Description |
|---|---|---|---|
| `discount-code` | string | — | Promotional discount code to apply |
| `translations` | JSON string | — | Key/value map of translated strings (see Translations section) |
| `show-only-highlights` | `"true"` / `"false"` | `"false"` | Show only highlighted features in the plan cards |
| `default-interval` | `"monthly"` / `"yearly"` | `"yearly"` | Which billing interval tab to open by default. Overridden by the store's active plan interval if one exists. |
| `large-card` | `"true"` / `"false"` | `"false"` | Show one fewer plan per view at wide screens. Use when your plan cards are content-heavy and need more horizontal space. At viewports ≥ 1440px the slider shows 1 fewer column than usual (5→4, 4→3). Has no effect below 1440px. |
| `show-free-on-yearly` | `"true"` / `"false"` | `"false"` | Show the Free plan on the Yearly tab. By default the Free plan only appears on the Monthly tab. Set to `"true"` to make it visible on both tabs. |

> **Boolean attributes** (`show-only-highlights`, `large-card`, `show-free-on-yearly`): follow HTML boolean attribute conventions — **presence activates them**. Only `="false"` explicitly disables. Everything else (bare attribute, `=""`, `="true"`, any value) is treated as `true`. Not passing the attribute defaults to `false`.

#### `default-interval` tab selection priority

The opening tab is determined in this order:

1. **No annual plans in API** → always opens Monthly (no yearly tab shown)
2. **Store has an active subscription** → opens on that plan's interval (monthly or yearly)
3. **`default-interval` attribute** → uses the value you set (`"monthly"` or `"yearly"`)
4. **Nothing matched** → falls back to `"yearly"` (the attribute's own default)

#### Example with all optional attributes

```html
<app-manager-billing-page
  base-url="https://your-api.example.com"
  shop-domain="your-store.myshopify.com"
  host="your-host"
  default-interval="monthly"
  large-card
  show-free-on-yearly
  show-only-highlights="true"
  discount-code="SAVE20"
></app-manager-billing-page>
```

---

### Translations

Both components accept a `translations` attribute — a JSON string mapping English keys to their translated values. Any key not provided falls back to the English default.

```html
<app-manager-billing-page
  base-url="..."
  shop-domain="..."
  host="..."
  translations='{
    "Most popular": "Am beliebtesten",
    "Choose plan": "Plan wählen",
    "Monthly": "Monatlich",
    "Yearly": "Jährlich",
    "Frequently asked questions": "Häufig gestellte Fragen"
  }'
></app-manager-billing-page>
```

Both **static UI labels** (e.g. "Choose plan", "Monthly") and **dynamic API content** (e.g. plan names, feature names, FAQ questions and answers, plan details, badges) pass through the translation layer and can be overridden via this object.

#### Debugging translations — `window.__APP_MANAGER_TRANSLATIONS__`

After the component renders, a `Set` of every translation key encountered on the page is available on the window object:

```js
window.__APP_MANAGER_TRANSLATIONS__
// Set(45) {
//   "Most popular",
//   "Choose plan",
//   "Basic Plan",                  ← plan name (dynamic, from API)
//   "Unlimited storage",           ← feature name (dynamic, from API)
//   "How does billing work?",      ← FAQ question (dynamic, from API)
//   ...
// }

// Convert to a plain array for easier reading:
[...window.__APP_MANAGER_TRANSLATIONS__]
```

Use this to verify that every key your app needs to translate is being captured. Cross-check it against the `translations` object you are passing — any key present in the Set but missing from your translations object will render in English.

---

### Notes
- This SDK does not require or load any external UI libraries by default.
- For `<app-manager-billing-page-polaris>`, you must include the Polaris script as shown above if your app does not already include it.
