@metrifox/angular-sdk 0.0.2-beta.1 → 0.0.2-beta.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/README.md +310 -320
- package/dist/README.md +310 -320
- package/dist/fesm2022/index.mjs +119 -64
- package/dist/modules/customer-portal/components/customer-portal.component.d.ts +8 -3
- package/dist/modules/pricing-table/components/pricing-table.component.d.ts +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,46 +1,44 @@
|
|
|
1
|
-
#
|
|
1
|
+
# MetriFox Angular SDK
|
|
2
2
|
|
|
3
3
|
A fully-configurable **Angular SDK** providing ready-to-use components for SaaS and billing platforms including customer portals, pricing tables, and more.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
[](https://github.com/metrifox/metrifox-angular-sdk/blob/main/LICENSE)
|
|
7
|
+
## ⚠️ Version notice – breaking changes
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
**If you are upgrading from an earlier version of the SDK, please read this.**
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
- 📦 **Standalone Components** - Modern Angular standalone components (no NgModule required)
|
|
14
|
-
- 🔥 **TypeScript** - Full type safety with comprehensive TypeScript definitions
|
|
15
|
-
- 🚀 **Easy Integration** - Simple setup with minimal configuration
|
|
16
|
-
- 📱 **Responsive** - Mobile-friendly components out of the box
|
|
17
|
-
- ♿ **Accessible** - WCAG compliant components
|
|
18
|
-
- 🌙 **Dark Mode** - Built-in dark mode support
|
|
19
|
-
- ⚡ **Tree-shakeable** - Only import what you need
|
|
11
|
+
This release introduces a **new theme structure** for both **Customer Portal** and **Pricing Table**. The theme API has been reorganized and is **not backward compatible** with previous versions.
|
|
20
12
|
|
|
21
|
-
|
|
13
|
+
- **Theme is now a single object** passed via `MetrifoxService.initialize({ theme })` with two keys: `customerPortal` and `pricingTable`. The previous top-level `pricingTableTheme` option is **deprecated**; use `theme.pricingTable` instead.
|
|
14
|
+
- **Customer Portal theme** uses a new grouped shape: `general`, `tabs`, `sections`, `buttons`, `lineItems`, `tables`, `modals`, and `plans`. Property names and nesting have changed from older versions.
|
|
15
|
+
- **Pricing Table theme** now strictly follows the same nested structure as Customer Portal: all plan-related keys must be under `plans` (e.g. `plans.planCards`, `plans.planToggle`). Top-level plan keys are no longer supported.
|
|
16
|
+
- **Both components** accept an optional `theme` input per instance to override or extend the global theme from `MetrifoxService.initialize`.
|
|
22
17
|
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
If you were using custom themes before, you will need to **migrate your theme objects** to the new structure. Omit any keys you don't need; the SDK merges your values with defaults. See the [Styling](#styling) section below for the full theme shapes and examples.
|
|
19
|
+
|
|
20
|
+
---
|
|
25
21
|
|
|
26
22
|
## Installation
|
|
27
23
|
|
|
28
24
|
```bash
|
|
29
|
-
#
|
|
30
|
-
pnpm add @metrifox/angular-sdk
|
|
31
|
-
|
|
32
|
-
# Using npm
|
|
25
|
+
# using npm
|
|
33
26
|
npm install @metrifox/angular-sdk
|
|
34
27
|
|
|
35
|
-
#
|
|
28
|
+
# or pnpm
|
|
29
|
+
pnpm add @metrifox/angular-sdk
|
|
30
|
+
|
|
31
|
+
# or yarn
|
|
36
32
|
yarn add @metrifox/angular-sdk
|
|
37
33
|
```
|
|
38
34
|
|
|
39
|
-
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Setup
|
|
40
38
|
|
|
41
|
-
### 1. Provide the SDK
|
|
39
|
+
### 1. Provide the SDK
|
|
42
40
|
|
|
43
|
-
|
|
41
|
+
Register the SDK providers in your app config:
|
|
44
42
|
|
|
45
43
|
```typescript
|
|
46
44
|
// app.config.ts (Standalone)
|
|
@@ -56,7 +54,7 @@ export const appConfig: ApplicationConfig = {
|
|
|
56
54
|
|
|
57
55
|
### 2. Initialize the SDK
|
|
58
56
|
|
|
59
|
-
|
|
57
|
+
Before using any components, initialize the SDK once with your Client key:
|
|
60
58
|
|
|
61
59
|
```typescript
|
|
62
60
|
import { MetrifoxService } from "@metrifox/angular-sdk";
|
|
@@ -66,22 +64,6 @@ MetrifoxService.initialize({
|
|
|
66
64
|
});
|
|
67
65
|
```
|
|
68
66
|
|
|
69
|
-
Or using the module approach:
|
|
70
|
-
|
|
71
|
-
```typescript
|
|
72
|
-
// app.module.ts
|
|
73
|
-
import { MetrifoxModule } from "@metrifox/angular-sdk";
|
|
74
|
-
|
|
75
|
-
@NgModule({
|
|
76
|
-
imports: [
|
|
77
|
-
MetrifoxModule.forRoot({
|
|
78
|
-
clientKey: "your-metrifox-client-key",
|
|
79
|
-
}),
|
|
80
|
-
],
|
|
81
|
-
})
|
|
82
|
-
export class AppModule {}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
67
|
### 3. Import Styles
|
|
86
68
|
|
|
87
69
|
Add the SDK styles to your `angular.json`:
|
|
@@ -98,345 +80,343 @@ Or import in your global `styles.css`:
|
|
|
98
80
|
@import "@metrifox/angular-sdk/styles.css";
|
|
99
81
|
```
|
|
100
82
|
|
|
101
|
-
|
|
83
|
+
> Compatible with **Angular 17, 18, and 19**. All components are standalone (no NgModule required).
|
|
102
84
|
|
|
103
|
-
|
|
104
|
-
import { Component } from "@angular/core";
|
|
105
|
-
import { CustomerPortalComponent } from "@metrifox/angular-sdk";
|
|
106
|
-
|
|
107
|
-
@Component({
|
|
108
|
-
selector: "app-billing",
|
|
109
|
-
standalone: true,
|
|
110
|
-
imports: [CustomerPortalComponent],
|
|
111
|
-
template: ` <metrifox-customer-portal [customerKey]="'cust_123'" /> `,
|
|
112
|
-
})
|
|
113
|
-
export class BillingComponent {}
|
|
114
|
-
```
|
|
85
|
+
---
|
|
115
86
|
|
|
116
|
-
##
|
|
87
|
+
## Widgets
|
|
117
88
|
|
|
118
|
-
|
|
89
|
+
## `CustomerPortal`
|
|
119
90
|
|
|
120
|
-
Displays a customizable customer dashboard with plans, subscriptions, billing, and more.
|
|
91
|
+
Displays a customizable customer dashboard with plans, subscriptions, billing, credits, and more.
|
|
121
92
|
|
|
122
93
|
```typescript
|
|
94
|
+
import { Component } from "@angular/core";
|
|
123
95
|
import { CustomerPortalComponent, SectionConfig } from "@metrifox/angular-sdk";
|
|
124
96
|
|
|
125
97
|
@Component({
|
|
98
|
+
selector: "app-billing",
|
|
126
99
|
standalone: true,
|
|
127
100
|
imports: [CustomerPortalComponent],
|
|
128
101
|
template: `
|
|
129
102
|
<metrifox-customer-portal
|
|
130
|
-
[customerKey]="
|
|
103
|
+
[customerKey]="'your-customer-key'"
|
|
131
104
|
[sectionsConfig]="sections"
|
|
132
|
-
|
|
133
|
-
(error)="onError($event)"
|
|
134
|
-
>
|
|
135
|
-
</metrifox-customer-portal>
|
|
105
|
+
/>
|
|
136
106
|
`,
|
|
137
107
|
})
|
|
138
|
-
export class
|
|
139
|
-
customerKey = "cust_123";
|
|
140
|
-
|
|
108
|
+
export class BillingComponent {
|
|
141
109
|
sections: SectionConfig[] = [
|
|
142
110
|
{ key: "subscription" },
|
|
143
111
|
{ key: "plan" },
|
|
144
|
-
{ key: "
|
|
145
|
-
{ key: "billingHistory" },
|
|
146
|
-
{ key: "paymentOverview" },
|
|
147
|
-
{ key: "entitlementUsage" },
|
|
148
|
-
{ key: "walletBalance" },
|
|
112
|
+
{ key: "billingHistory", hidden: true },
|
|
149
113
|
];
|
|
150
|
-
|
|
151
|
-
onDataLoaded(data: CustomerData) {
|
|
152
|
-
console.log("Customer data loaded:", data);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
onError(error: MetrifoxErrorEvent) {
|
|
156
|
-
console.error("Error:", error);
|
|
157
|
-
}
|
|
158
114
|
}
|
|
159
115
|
```
|
|
160
116
|
|
|
161
|
-
|
|
117
|
+
Optional **`theme`** input: pass a `CustomerPortalTheme` object to override or extend the global theme from `MetrifoxService.initialize` for this instance only.
|
|
118
|
+
|
|
119
|
+
#### Section Configuration
|
|
120
|
+
|
|
121
|
+
The `sectionsConfig` input controls what appears in the portal.
|
|
122
|
+
|
|
123
|
+
| Property | Type | Description |
|
|
124
|
+
| ----------- | ----------------- | --------------------------------------------------- |
|
|
125
|
+
| `key` | `SectionKey` | Unique key of the section (see list below) |
|
|
126
|
+
| `hidden` | `boolean` | Hide this section when `true` |
|
|
127
|
+
| `component` | `Type<any>` | Replace the default section with your own component |
|
|
128
|
+
| `props` | `Record<string, unknown>` | Extra props passed to the custom or default section |
|
|
162
129
|
|
|
163
|
-
|
|
164
|
-
| ---------------- | ----------------- | -------- | ---------------------------------- |
|
|
165
|
-
| `customerKey` | `string` | Yes | Unique identifier for the customer |
|
|
166
|
-
| `sectionsConfig` | `SectionConfig[]` | No | Configuration for portal sections |
|
|
130
|
+
#### Built-in Section Keys
|
|
167
131
|
|
|
168
|
-
|
|
132
|
+
| Key | Description |
|
|
133
|
+
| ------------------ | ----------------------------- |
|
|
134
|
+
| `upcomingInvoice` | Displays next invoice details |
|
|
135
|
+
| `subscription` | Active subscription overview |
|
|
136
|
+
| `creditBalance` | Shows user wallet balance |
|
|
137
|
+
| `entitlementUsage` | Displays resource usage |
|
|
138
|
+
| `paymentOverview` | Payment summary and methods |
|
|
139
|
+
| `billingHistory` | List of past transactions |
|
|
140
|
+
| `plan` | Current plan details |
|
|
169
141
|
|
|
170
|
-
|
|
171
|
-
| ------------ | -------------------- | ------------------------------------ |
|
|
172
|
-
| `dataLoaded` | `CustomerData` | Emitted when customer data is loaded |
|
|
173
|
-
| `error` | `MetrifoxErrorEvent` | Emitted when an error occurs |
|
|
142
|
+
#### Section Anchors
|
|
174
143
|
|
|
175
|
-
|
|
144
|
+
Each portal section renders inside a `<section id="...">` wrapper so you can link directly to segments of a customer portal view. The default anchor IDs are:
|
|
176
145
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
- `upcomingInvoice`
|
|
180
|
-
|
|
181
|
-
- `
|
|
182
|
-
- `entitlementUsage`
|
|
183
|
-
- `
|
|
146
|
+
| Anchor ID | Section Key |
|
|
147
|
+
| -------------------- | ------------------ |
|
|
148
|
+
| `#upcoming-invoice` | `upcomingInvoice` |
|
|
149
|
+
| `#subscription` | `subscription` |
|
|
150
|
+
| `#credit-balance` | `creditBalance` |
|
|
151
|
+
| `#entitlement-usage` | `entitlementUsage` |
|
|
152
|
+
| `#payment-overview` | `paymentOverview` |
|
|
153
|
+
| `#billing-history` | `billingHistory` |
|
|
154
|
+
| `#plan` | `plan` |
|
|
184
155
|
|
|
185
|
-
|
|
156
|
+
Use these anchors when embedding the SDK or sharing deep links (e.g., `https://app.example.com/portal#billing-history`).
|
|
186
157
|
|
|
187
|
-
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Pricing Table
|
|
161
|
+
|
|
162
|
+
Displays subscription plans and one-time purchases in a configurable pricing table component.
|
|
188
163
|
|
|
189
164
|
```typescript
|
|
190
|
-
import {
|
|
165
|
+
import { Component } from "@angular/core";
|
|
166
|
+
import { PricingTableComponent } from "@metrifox/angular-sdk";
|
|
191
167
|
|
|
192
168
|
@Component({
|
|
169
|
+
selector: "app-pricing",
|
|
193
170
|
standalone: true,
|
|
194
171
|
imports: [PricingTableComponent],
|
|
195
172
|
template: `
|
|
196
173
|
<metrifox-pricing-table
|
|
197
|
-
[checkoutUsername]="username"
|
|
198
|
-
[productKey]="
|
|
199
|
-
|
|
200
|
-
[showTabHeader]="true"
|
|
201
|
-
(planSelected)="onPlanSelect($event)"
|
|
202
|
-
(purchaseSelected)="onPurchaseSelect($event)"
|
|
203
|
-
>
|
|
204
|
-
</metrifox-pricing-table>
|
|
174
|
+
[checkoutUsername]="'your-checkout-username'"
|
|
175
|
+
[productKey]="'your-product-key'"
|
|
176
|
+
/>
|
|
205
177
|
`,
|
|
206
178
|
})
|
|
207
|
-
export class PricingComponent {
|
|
208
|
-
|
|
209
|
-
productKey = "your-product-key";
|
|
179
|
+
export class PricingComponent {}
|
|
180
|
+
```
|
|
210
181
|
|
|
211
|
-
|
|
212
|
-
console.log("Plan selected:", event.plan);
|
|
213
|
-
console.log("Action:", event.action); // 'upgrade' | 'downgrade' | 'new' | 'current'
|
|
214
|
-
}
|
|
182
|
+
---
|
|
215
183
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
184
|
+
#### Props
|
|
185
|
+
|
|
186
|
+
The inputs control how the pricing table is configured.
|
|
187
|
+
|
|
188
|
+
| Property | Type | Required | Default | Description |
|
|
189
|
+
| --------------------- | ------------------ | -------- | ------- | -------------------------------------------------------------------------------- |
|
|
190
|
+
| `checkoutUsername` | `string` | Yes | — | Unique username used for checkout. This can be found in **Settings → Checkout**. |
|
|
191
|
+
| `productKey` | `string` | Yes | — | Unique product identifier. This can be found on the product page. |
|
|
192
|
+
| `plansOnly` | `boolean` | No | `false` | Controls whether only subscription plans are rendered. |
|
|
193
|
+
| `singlePurchasesOnly` | `boolean` | No | `false` | Controls whether only single purchases are rendered. |
|
|
194
|
+
| `showTabHeader` | `boolean` | No | `true` | Controls whether the tab header for switching between offerings is rendered. |
|
|
195
|
+
| `theme` | `PricingTableTheme`| No | — | Optional theme override for this instance (merged with global theme from `MetrifoxService.initialize`). |
|
|
196
|
+
|
|
197
|
+
> **Note:** If both `plansOnly` and `singlePurchasesOnly` are `false` or undefined, both plans and single purchases are displayed.
|
|
198
|
+
|
|
199
|
+
## Styling
|
|
200
|
+
|
|
201
|
+
Import the SDK's global styles (see [Setup](#setup) step 3).
|
|
202
|
+
|
|
203
|
+
> This is required for proper styling of all components.
|
|
204
|
+
|
|
205
|
+
### Theme configuration (new structure)
|
|
206
|
+
|
|
207
|
+
Theming is driven by a single `theme` object passed to `MetrifoxService.initialize`. It has two top-level keys: `customerPortal` and `pricingTable`. Any value you omit falls back to the SDK default. You can also pass an optional `theme` input to `<metrifox-customer-portal>` or `<metrifox-pricing-table>` to override or extend the global theme for that instance.
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
// Passed to MetrifoxService.initialize({ theme })
|
|
211
|
+
theme?: {
|
|
212
|
+
customerPortal?: CustomerPortalTheme
|
|
213
|
+
pricingTable?: PricingTableTheme
|
|
219
214
|
}
|
|
220
215
|
```
|
|
221
216
|
|
|
222
|
-
|
|
217
|
+
> **Deprecated:** The previous root-level `pricingTableTheme` option is no longer supported. Use `theme.pricingTable` instead.
|
|
223
218
|
|
|
224
|
-
|
|
225
|
-
| --------------------- | --------- | -------- | ------- | -------------------------------------------- |
|
|
226
|
-
| `checkoutUsername` | `string` | Yes | - | Username for checkout |
|
|
227
|
-
| `productKey` | `string` | Yes | - | Product identifier |
|
|
228
|
-
| `plansOnly` | `boolean` | No | `false` | Show only subscription plans |
|
|
229
|
-
| `singlePurchasesOnly` | `boolean` | No | `false` | Show only one-time purchases |
|
|
230
|
-
| `showTabHeader` | `boolean` | No | `true` | Show tab header for switching |
|
|
231
|
-
| `customerKey` | `string` | No | - | Current customer (to highlight current plan) |
|
|
219
|
+
---
|
|
232
220
|
|
|
233
|
-
|
|
221
|
+
### Customer Portal theme (`CustomerPortalTheme`)
|
|
234
222
|
|
|
235
|
-
|
|
236
|
-
| ------------------ | --------------------- | ----------------------------------- |
|
|
237
|
-
| `planSelected` | `PlanSelectEvent` | Emitted when a plan is selected |
|
|
238
|
-
| `purchaseSelected` | `PurchaseSelectEvent` | Emitted when a purchase is selected |
|
|
239
|
-
| `dataLoaded` | `PricingData` | Emitted when pricing data is loaded |
|
|
240
|
-
| `error` | `MetrifoxErrorEvent` | Emitted when an error occurs |
|
|
223
|
+
All properties are optional. The shape is grouped by area of the UI:
|
|
241
224
|
|
|
242
|
-
|
|
225
|
+
| Group | Description |
|
|
226
|
+
| ----- | ----------- |
|
|
227
|
+
| `general` | Page-level: link color, background, border radius, font family, container padding |
|
|
228
|
+
| `tabs` | Tab bar (e.g. Wallet balance tabs): background, border, active/inactive states |
|
|
229
|
+
| `sections` | Section cards: background, padding, borders, content/summaryBalance sub-styles, header/label/value typography, usage bars, empty text |
|
|
230
|
+
| `buttons` | Primary and secondary buttons: background, border (color/width/radius), typography |
|
|
231
|
+
| `lineItems` | Subscription line items: parentRow/childRow background, border, typography (label/quantity) |
|
|
232
|
+
| `tables` | Tables (e.g. billing history): header/row colors, border, cell padding, expand icon, typography |
|
|
233
|
+
| `modals` | Modal overlay, background, border, close button; header/title/description typography; footer primary/secondary buttons |
|
|
234
|
+
| `plans` | Plan cards when shown in portal: currentPlanCard, planCards (border as `{ color, width, radius }`), planFeatures, planButton, planToggle, planTags |
|
|
243
235
|
|
|
244
|
-
|
|
236
|
+
**Example – minimal override:**
|
|
245
237
|
|
|
246
238
|
```typescript
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
activeTextColor: "#3b82f6",
|
|
256
|
-
inactiveTextColor: "#9ca3af",
|
|
257
|
-
},
|
|
258
|
-
card: {
|
|
259
|
-
titleBackground: "#1f2937",
|
|
260
|
-
contentBackground: "#111827",
|
|
261
|
-
titleColor: "#f9fafb",
|
|
262
|
-
details: {
|
|
263
|
-
labelColor: "#cbd5e1",
|
|
264
|
-
valueColor: "#ffffff",
|
|
265
|
-
},
|
|
266
|
-
},
|
|
267
|
-
button: {
|
|
268
|
-
background: "#3b82f6",
|
|
269
|
-
textColor: "#ffffff",
|
|
270
|
-
},
|
|
239
|
+
customerPortal: {
|
|
240
|
+
general: { linkColor: "#2563eb", backgroundColor: "#ffffff" },
|
|
241
|
+
tabs: {
|
|
242
|
+
tabBackground: "#ffffff",
|
|
243
|
+
tabBorderColor: "#e5e7eb",
|
|
244
|
+
activeTabBackground: "#2563eb",
|
|
245
|
+
activeTabTextColor: "#ffffff",
|
|
246
|
+
inactiveTabTextColor: "#6b7280",
|
|
271
247
|
},
|
|
272
|
-
|
|
273
|
-
|
|
248
|
+
sections: {
|
|
249
|
+
background: "#ffffff",
|
|
250
|
+
content: { background: "#f4f4f5", borderRadius: "8px" },
|
|
251
|
+
header: { fontSize: "16px", fontWeight: "600", color: "#52525b" },
|
|
252
|
+
label: { fontSize: "13px", color: "#71717a" },
|
|
253
|
+
value: { fontSize: "16px", color: "#52525b" },
|
|
254
|
+
},
|
|
255
|
+
buttons: {
|
|
256
|
+
primary: { backgroundColor: "#2563eb", border: { radius: "8px" }, typography: { color: "#ffffff" } },
|
|
257
|
+
secondary: { backgroundColor: "#e4e4e7", typography: { color: "#3f3f46" } },
|
|
258
|
+
},
|
|
259
|
+
plans: {
|
|
260
|
+
planCards: {
|
|
274
261
|
background: "#ffffff",
|
|
275
|
-
|
|
276
|
-
header: {
|
|
277
|
-
|
|
278
|
-
},
|
|
279
|
-
price: {
|
|
280
|
-
amountColor: "#111827",
|
|
281
|
-
},
|
|
282
|
-
},
|
|
283
|
-
button: {
|
|
284
|
-
background: "#2563eb",
|
|
285
|
-
textColor: "#ffffff",
|
|
262
|
+
border: { color: "#e5e7eb", width: "1px", radius: "8px" },
|
|
263
|
+
header: { background: "#e5e7eb", textColor: "#111827" },
|
|
264
|
+
description: { textColor: "#6b7280", textButtonColor: "#2563eb" },
|
|
265
|
+
price: { amountColor: "#111827", primaryTextColor: "#6b7280", secondaryTextColor: "#9ca3af" },
|
|
286
266
|
},
|
|
267
|
+
planButton: { background: "#2563eb", textColor: "#ffffff" },
|
|
268
|
+
planToggle: { background: "#e5e7eb", activeBackground: "#1f2937", activeText: "#ffffff", inactiveText: "#6b7280" },
|
|
287
269
|
},
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
MetrifoxService.initialize({
|
|
291
|
-
clientKey: "your-client-key",
|
|
292
|
-
theme,
|
|
293
|
-
});
|
|
270
|
+
}
|
|
294
271
|
```
|
|
295
272
|
|
|
296
|
-
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
### Font customization
|
|
297
276
|
|
|
298
|
-
The SDK
|
|
277
|
+
The SDK accepts **any font-family string** in your theme. The SDK applies it via CSS, but **your app must load the font** (Google Fonts, @font-face, etc.) before the SDK renders.
|
|
299
278
|
|
|
300
|
-
|
|
301
|
-
|
|
279
|
+
**How it works:**
|
|
280
|
+
|
|
281
|
+
1. **Load the font in your app** (HTML, CSS, or framework-specific):
|
|
302
282
|
|
|
303
283
|
```html
|
|
304
|
-
<!--
|
|
305
|
-
<
|
|
306
|
-
<metrifox-customer-portal [customerKey]="key" />
|
|
307
|
-
</div>
|
|
284
|
+
<!-- Option A: Google Fonts in your index.html <head> -->
|
|
285
|
+
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
|
|
308
286
|
```
|
|
309
287
|
|
|
310
|
-
|
|
288
|
+
```css
|
|
289
|
+
/* Option B: @font-face in your global CSS */
|
|
290
|
+
@font-face {
|
|
291
|
+
font-family: 'MyCustomFont';
|
|
292
|
+
src: url('/fonts/custom.woff2') format('woff2');
|
|
293
|
+
}
|
|
294
|
+
```
|
|
311
295
|
|
|
312
|
-
|
|
296
|
+
2. **Pass the font-family to the SDK theme:**
|
|
313
297
|
|
|
314
298
|
```typescript
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
next: (data) => console.log('Customer:', data),
|
|
325
|
-
error: (err) => console.error('Error:', err)
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
updateSubscription() {
|
|
330
|
-
this.metrifoxService.updateSubscription('cust_123', {
|
|
331
|
-
planId: 'plan_pro',
|
|
332
|
-
proration: 'create_prorations'
|
|
333
|
-
}).subscribe({
|
|
334
|
-
next: (result) => {
|
|
335
|
-
if (result.checkoutUrl) {
|
|
336
|
-
window.location.href = result.checkoutUrl;
|
|
337
|
-
}
|
|
299
|
+
MetrifoxService.initialize({
|
|
300
|
+
clientKey: "your-client-key",
|
|
301
|
+
theme: {
|
|
302
|
+
customerPortal: {
|
|
303
|
+
general: {
|
|
304
|
+
// Any font-family string works
|
|
305
|
+
fontFamily: '"Space Grotesk", "Inter", sans-serif'
|
|
306
|
+
// or: 'MyCustomFont, sans-serif'
|
|
307
|
+
// or: 'system-ui, -apple-system, sans-serif'
|
|
338
308
|
}
|
|
339
|
-
}
|
|
309
|
+
}
|
|
340
310
|
}
|
|
311
|
+
});
|
|
312
|
+
```
|
|
341
313
|
|
|
342
|
-
|
|
343
|
-
this.metrifoxService.checkAccess('cust_123', 'api_calls').subscribe({
|
|
344
|
-
next: ({ hasAccess, remaining, limit }) => {
|
|
345
|
-
console.log(`Access: ${hasAccess}, Remaining: ${remaining}/${limit}`);
|
|
346
|
-
}
|
|
347
|
-
});
|
|
348
|
-
}
|
|
314
|
+
If no `fontFamily` is provided, the SDK inherits the font from the host page. This means the SDK works with whatever font your app already uses — no extra configuration needed.
|
|
349
315
|
|
|
350
|
-
|
|
351
|
-
this.metrifoxService.recordUsage('cust_123', 'api_calls', 1).subscribe({
|
|
352
|
-
next: () => console.log('Usage recorded')
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
```
|
|
316
|
+
> **Important:** When using Google Fonts or custom fonts, load them in your app **before** the SDK initializes to prevent flash of unstyled text (FOUT).
|
|
357
317
|
|
|
358
|
-
|
|
318
|
+
---
|
|
359
319
|
|
|
360
|
-
|
|
320
|
+
### Pricing Table theme (`PricingTableTheme`)
|
|
361
321
|
|
|
362
|
-
|
|
363
|
-
// my-custom-plan.component.ts
|
|
364
|
-
import { Component, Input } from "@angular/core";
|
|
365
|
-
import {
|
|
366
|
-
CustomSectionComponentBase,
|
|
367
|
-
CustomerData,
|
|
368
|
-
} from "@metrifox/angular-sdk";
|
|
322
|
+
The Pricing Table theme follows the same nested structure as Customer Portal. All plan-related keys must be nested under `plans`.
|
|
369
323
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
@Input() customerData?: CustomerData;
|
|
382
|
-
@Input() customerKey?: string;
|
|
383
|
-
@Input() customProp?: string;
|
|
384
|
-
}
|
|
324
|
+
| Key | Description |
|
|
325
|
+
| --- | ----------- |
|
|
326
|
+
| `plans` | Container for all plan-related styling |
|
|
327
|
+
| `plans.currentPlanCard` | Current-plan highlight: header (background, textColor), gradientColor, description, borderRadius |
|
|
328
|
+
| `plans.planCards` | Card style: background, border (`{ color, width, radius }`), header, description (textColor, textButtonColor), price (amountColor, primaryTextColor, secondaryTextColor, textButtonColor, background, borderColor) |
|
|
329
|
+
| `plans.planFeatures` | Feature list: textColor, iconColor |
|
|
330
|
+
| `plans.planButton` | CTA button: background, textColor; optional secondaryBackground, secondaryTextColor, textButtonColor |
|
|
331
|
+
| `plans.planToggle` | Monthly/Yearly toggle: background, activeBackground, activeText, inactiveText |
|
|
332
|
+
| `plans.planTags` | Tags (e.g. free trial): freeTrialBackground, freeTrialText |
|
|
333
|
+
| `tabs` | Tabs (e.g. Plans vs Single purchases): inactiveText, activeText, indicator, borderColor |
|
|
334
|
+
| `checkoutBar` | Bottom checkout bar: background, borderColor, textColor, buttonBackground, buttonTextColor |
|
|
385
335
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
sections: SectionConfig[] = [
|
|
398
|
-
{ key: "subscription" },
|
|
399
|
-
{
|
|
400
|
-
key: "plan",
|
|
401
|
-
component: MyCustomPlanComponent,
|
|
402
|
-
props: { customProp: "Hello!" },
|
|
336
|
+
**Example – minimal override:**
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
pricingTable: {
|
|
340
|
+
plans: {
|
|
341
|
+
planCards: {
|
|
342
|
+
background: "#ffffff",
|
|
343
|
+
border: { color: "#e5e7eb", width: "1px", radius: "8px" },
|
|
344
|
+
header: { background: "#e5e7eb", textColor: "#111827" },
|
|
345
|
+
description: { textColor: "#6b7280", textButtonColor: "#2563eb" },
|
|
346
|
+
price: { amountColor: "#111827", primaryTextColor: "#6b7280", secondaryTextColor: "#9ca3af" },
|
|
403
347
|
},
|
|
404
|
-
|
|
348
|
+
planButton: { background: "#2563eb", textColor: "#ffffff" },
|
|
349
|
+
planToggle: { background: "#e5e7eb", activeBackground: "#1f2937", activeText: "#ffffff", inactiveText: "#6b7280" },
|
|
350
|
+
},
|
|
351
|
+
tabs: { activeText: "#2563eb", indicator: "#2563eb", borderColor: "#9ca3af" },
|
|
352
|
+
checkoutBar: {
|
|
353
|
+
background: "#f9fafb",
|
|
354
|
+
borderColor: "#e5e7eb",
|
|
355
|
+
textColor: "#3f3f46",
|
|
356
|
+
buttonBackground: "#2563eb",
|
|
357
|
+
buttonTextColor: "#ffffff",
|
|
358
|
+
},
|
|
405
359
|
}
|
|
406
360
|
```
|
|
407
361
|
|
|
408
|
-
|
|
362
|
+
When the Pricing Table is embedded inside the Customer Portal, it automatically uses `theme.customerPortal.plans` for plan styling so both components stay consistent.
|
|
363
|
+
|
|
364
|
+
---
|
|
409
365
|
|
|
410
|
-
|
|
366
|
+
### Full example
|
|
411
367
|
|
|
412
368
|
```typescript
|
|
413
|
-
import
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
369
|
+
import { MetrifoxService } from "@metrifox/angular-sdk";
|
|
370
|
+
|
|
371
|
+
MetrifoxService.initialize({
|
|
372
|
+
clientKey: "your-client-key",
|
|
373
|
+
theme: {
|
|
374
|
+
customerPortal: {
|
|
375
|
+
general: { linkColor: "#2563eb", backgroundColor: "#ffffff" },
|
|
376
|
+
tabs: {
|
|
377
|
+
tabBackground: "#ffffff",
|
|
378
|
+
activeTabBackground: "#2563eb",
|
|
379
|
+
activeTabTextColor: "#ffffff",
|
|
380
|
+
inactiveTabTextColor: "#6b7280",
|
|
381
|
+
},
|
|
382
|
+
sections: {
|
|
383
|
+
background: "#ffffff",
|
|
384
|
+
content: { background: "#f4f4f5", borderRadius: "8px" },
|
|
385
|
+
},
|
|
386
|
+
buttons: {
|
|
387
|
+
primary: { backgroundColor: "#2563eb", typography: { color: "#ffffff" } },
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
pricingTable: {
|
|
391
|
+
plans: {
|
|
392
|
+
planCards: { background: "#ffffff", border: { color: "#e5e7eb", radius: "8px" } },
|
|
393
|
+
planButton: { background: "#2563eb", textColor: "#ffffff" },
|
|
394
|
+
},
|
|
395
|
+
tabs: { activeText: "#2563eb", indicator: "#2563eb" },
|
|
396
|
+
checkoutBar: { buttonBackground: "#2563eb", buttonTextColor: "#ffffff" },
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
});
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Per-component overrides (optional):
|
|
403
|
+
|
|
404
|
+
```html
|
|
405
|
+
<metrifox-customer-portal
|
|
406
|
+
[customerKey]="'cust_123'"
|
|
407
|
+
[theme]="{ general: { linkColor: '#1d4ed8' } }"
|
|
408
|
+
/>
|
|
409
|
+
|
|
410
|
+
<metrifox-pricing-table
|
|
411
|
+
[checkoutUsername]="'checkout_user'"
|
|
412
|
+
[productKey]="'prod_key'"
|
|
413
|
+
[theme]="{ plans: { planCards: { background: '#f8fafc' } } }"
|
|
414
|
+
/>
|
|
437
415
|
```
|
|
438
416
|
|
|
439
|
-
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
### Migration from React SDK
|
|
440
420
|
|
|
441
421
|
| React | Angular |
|
|
442
422
|
| ----------------------- | ------------------------------ |
|
|
@@ -445,43 +425,53 @@ import type {
|
|
|
445
425
|
| `<PricingTable />` | `<metrifox-pricing-table>` |
|
|
446
426
|
| `customerKey` prop | `[customerKey]` input |
|
|
447
427
|
| `onPlanSelect` callback | `(planSelected)` output |
|
|
428
|
+
| `theme` prop | `[theme]` input |
|
|
429
|
+
|
|
430
|
+
---
|
|
448
431
|
|
|
449
|
-
##
|
|
432
|
+
## Local Development
|
|
450
433
|
|
|
451
|
-
|
|
434
|
+
### Using [yalc](https://github.com/wclr/yalc)
|
|
452
435
|
|
|
453
|
-
|
|
436
|
+
For local SDK testing:
|
|
454
437
|
|
|
455
|
-
|
|
438
|
+
```bash
|
|
439
|
+
# In SDK project
|
|
440
|
+
pnpm build
|
|
441
|
+
npx yalc publish
|
|
442
|
+
|
|
443
|
+
# In consuming app
|
|
444
|
+
npx yalc add @metrifox/angular-sdk
|
|
445
|
+
npm install
|
|
446
|
+
```
|
|
456
447
|
|
|
457
|
-
|
|
458
|
-
pnpm install
|
|
459
|
-
pnpm run build
|
|
460
|
-
```
|
|
448
|
+
#### Auto-update during development
|
|
461
449
|
|
|
462
|
-
|
|
450
|
+
```bash
|
|
451
|
+
# In SDK project
|
|
452
|
+
pnpm build # rebuild
|
|
453
|
+
npx yalc publish # re-publish to yalc
|
|
463
454
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
npm run dev
|
|
468
|
-
```
|
|
455
|
+
# In consuming project
|
|
456
|
+
npx yalc update @metrifox/angular-sdk
|
|
457
|
+
```
|
|
469
458
|
|
|
470
|
-
|
|
459
|
+
#### Clean up
|
|
471
460
|
|
|
472
|
-
|
|
461
|
+
```bash
|
|
462
|
+
npx yalc remove @metrifox/angular-sdk
|
|
463
|
+
npm install
|
|
464
|
+
```
|
|
473
465
|
|
|
474
|
-
|
|
475
|
-
- Firefox (latest)
|
|
476
|
-
- Safari (latest)
|
|
477
|
-
- Edge (latest)
|
|
466
|
+
---
|
|
478
467
|
|
|
479
468
|
## Support
|
|
480
469
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
470
|
+
📘 **Full Documentation:**
|
|
471
|
+
For detailed guides, API references, and live examples, visit [docs.metrifox.com](https://docs.metrifox.com).
|
|
472
|
+
|
|
473
|
+
---
|
|
484
474
|
|
|
485
|
-
## License
|
|
475
|
+
## 📄 License
|
|
486
476
|
|
|
487
|
-
MIT © [
|
|
477
|
+
MIT © [MetriFox](https://metrifox.com)
|