@nauticana/sail 1.1.40 → 1.1.42
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 +16 -5
- package/package.json +1 -1
- package/src/component/billing/scheduled_change_banner.html +14 -0
- package/src/component/billing/scheduled_change_banner.ts +39 -0
- package/src/index.ts +1 -0
- package/src/model/appdata.ts +8 -0
- package/src/service/auth.interceptor.ts +8 -1
- package/src/service/billing.service.ts +6 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A shared Angular component library for building CRUD-based admin frontends. Provides table management, form handling, navigation, authentication, two-factor authentication, and trusted device management — all driven by metadata from a [keel](https://github.com/nauticana/keel) Go backend.
|
|
4
4
|
|
|
5
|
-
> **Compatibility:** sail and keel are versioned in lock-step. The current line is **sail v1.1.x ↔ keel v1.2.x**; sail v1.1.39 and v1.1.40 adopt keel v1.2.115 (usage captions, provider-managed subscriptions, the connect disconnect route), sail v1.1.37 adopts keel v1.2.114 (scopes validated against a constant list), sail v1.1.36 adopts keel v1.2.113 (API key expiry, caller-aware scope policy), sail v1.1.35 adopts keel v1.2.112 (minor-unit money, the `currency` catalog, OAuth grant routes), sail v1.1.33 adopts keel v1.2.106 (`prompt` on the login page, new-device step-up, sign-in sessions), sail v1.1.32 adopts keel v1.2.101 (the OAuth connect callback ticket), sail v1.1.31 adopts keel v1.2.100 (tenant single sign-on, DNS and HTTP-file domain verification), sail v1.1.30 adopts keel v1.2.99 (password routes; a password change signs out), sail v1.1.29 adopts keel v1.2.94 (signup: confirmation signs in, partner setup, sign-in hand-off code), sail v1.1.28 adopts keel v1.2.93 (absolute `return` for the OAuth hand-off), sail v1.1.18 adopts keel v1.2.58 (refresh-token rotation, `/public/logout`, setup intents, the realtime hub), the agency components in sail v1.1.14 require keel v1.2.41 (sail v1.1.9 needs keel v1.2.16 for the OAuth-connect layer). Earlier lines: **sail v0.5.x ↔ keel v0.5.x**, **sail v0.6.x / v0.7.x ↔ keel v0.7.x**, **sail v0.8.x ↔ keel v0.8.x**, **sail v0.9.x ↔ keel v0.9.x**. Newer sail releases extend the contract — older keel servers reject unknown endpoints with HTTP 404 / 400. The v0.8.x line additionally ships the `table_action` framework (per-table custom buttons surfaced in `TableList` / `TableSearch` / `TableEdit` / `TableDetail`); see the [Migrating to v0.7.0 §5 — TableAction](#migrating-to-v070--payout-user-payment-methods-table-actions) section for the seed shape (basis `table_action` + `authorization_object` + `authorization_object_action` rows) and the [keel/README Table Actions](https://github.com/nauticana/keel#table-actions) section for backend wiring via `handler.WrapTableAction`.
|
|
5
|
+
> **Compatibility:** sail and keel are versioned in lock-step. The current line is **sail v1.1.x ↔ keel v1.2.x**; sail v1.1.42 adopts keel v1.2.118 (RFC 3339 timestamps, `X-Time-Zone`), sail v1.1.41 adopts keel v1.2.117 (scheduled plan changes), sail v1.1.39 and v1.1.40 adopt keel v1.2.115 (usage captions, provider-managed subscriptions, the connect disconnect route), sail v1.1.37 adopts keel v1.2.114 (scopes validated against a constant list), sail v1.1.36 adopts keel v1.2.113 (API key expiry, caller-aware scope policy), sail v1.1.35 adopts keel v1.2.112 (minor-unit money, the `currency` catalog, OAuth grant routes), sail v1.1.33 adopts keel v1.2.106 (`prompt` on the login page, new-device step-up, sign-in sessions), sail v1.1.32 adopts keel v1.2.101 (the OAuth connect callback ticket), sail v1.1.31 adopts keel v1.2.100 (tenant single sign-on, DNS and HTTP-file domain verification), sail v1.1.30 adopts keel v1.2.99 (password routes; a password change signs out), sail v1.1.29 adopts keel v1.2.94 (signup: confirmation signs in, partner setup, sign-in hand-off code), sail v1.1.28 adopts keel v1.2.93 (absolute `return` for the OAuth hand-off), sail v1.1.18 adopts keel v1.2.58 (refresh-token rotation, `/public/logout`, setup intents, the realtime hub), the agency components in sail v1.1.14 require keel v1.2.41 (sail v1.1.9 needs keel v1.2.16 for the OAuth-connect layer). Earlier lines: **sail v0.5.x ↔ keel v0.5.x**, **sail v0.6.x / v0.7.x ↔ keel v0.7.x**, **sail v0.8.x ↔ keel v0.8.x**, **sail v0.9.x ↔ keel v0.9.x**. Newer sail releases extend the contract — older keel servers reject unknown endpoints with HTTP 404 / 400. The v0.8.x line additionally ships the `table_action` framework (per-table custom buttons surfaced in `TableList` / `TableSearch` / `TableEdit` / `TableDetail`); see the [Migrating to v0.7.0 §5 — TableAction](#migrating-to-v070--payout-user-payment-methods-table-actions) section for the seed shape (basis `table_action` + `authorization_object` + `authorization_object_action` rows) and the [keel/README Table Actions](https://github.com/nauticana/keel#table-actions) section for backend wiring via `handler.WrapTableAction`.
|
|
6
6
|
|
|
7
7
|
**Recent additions:** `BaseAuthService.acceptToken(jwt)` — adopt an externally-minted JWT (registration / SSO-handoff flows) and run the full post-login sequence (store under the canonical `jwt` key, load appdata, init routes); apps must use this instead of writing `localStorage` directly. `BaseRestService.analytic<T>(endpoint, params?)` — GET a keel `analytic/<endpoint>` report and return its rows. `passwordPolicyValidator` + `BaseAuthService.ensurePasswordPolicy()` — validate passwords against keel's policy (via `SailGuiConfig.passwordPolicyUrl`); see Configuration reference.
|
|
8
8
|
|
|
@@ -19,7 +19,7 @@ A shared Angular component library for building CRUD-based admin frontends. Prov
|
|
|
19
19
|
| **Security** | `TwoFactorSetupComponent`, `TwoFactorVerifyComponent`, `TrustedDevicesComponent`, `SessionsComponent`, `ApiKeysComponent`, `ConnectedAppsComponent`, `AccountDeletionComponent` |
|
|
20
20
|
| **Account** | `MyAccountComponent` (self-service hub), `ProfileEditorComponent` (name/locale immediate; email/phone verify-before-apply) |
|
|
21
21
|
| **Auth** | `ConsentGateComponent`, `OtpInputComponent`, `SocialLoginComponent`, `SsoLoginComponent`, `SsoReturnComponent` |
|
|
22
|
-
| **Billing** | `PlanSelectorComponent`, `PriceSelectorComponent`, `CheckoutButtonComponent`, `PaymentMethodsComponent`, `PortalButtonComponent`, `UsageMeterComponent`, `StatusChipComponent`, `TrialBannerComponent`, `SeatSelectorComponent`, `DunningBannerComponent` |
|
|
22
|
+
| **Billing** | `PlanSelectorComponent`, `PriceSelectorComponent`, `CheckoutButtonComponent`, `PaymentMethodsComponent`, `PortalButtonComponent`, `UsageMeterComponent`, `StatusChipComponent`, `TrialBannerComponent`, `ScheduledChangeBannerComponent`, `SeatSelectorComponent`, `DunningBannerComponent` |
|
|
23
23
|
| **Dashboard** | `DashboardShellComponent`, `DataCardComponent`, `StatTileComponent`, `LockedOverlayComponent`, `ActionCenterComponent`, `EntitySelectorComponent`, `VerificationFlowComponent` |
|
|
24
24
|
| **Payout** | `PayoutProviderOnboardingComponent`, `PayoutBankInfoFormComponent` |
|
|
25
25
|
| **Agency** | `AgencyClientsComponent`, `AgencyStatusBannerComponent`, `AgencyAcceptComponent`, `AgencyEarningsSummaryComponent`, `CommissionLedgerComponent`, `AgencyPayoutHistoryComponent`, `AgencyPayoutProfileComponent` |
|
|
@@ -29,7 +29,7 @@ A shared Angular component library for building CRUD-based admin frontends. Prov
|
|
|
29
29
|
| **Services** | `BaseAuthService` (OTP / social / push / deleteAccount / logoutEverywhere / profile: `getProfile`, `updateProfile`, `request`+`confirmEmailChange`, `request`+`confirmPhoneChange`), `BillingService`, `AgencyService`, `BackendService`, `PayoutService`, `UserPaymentMethodService`, `PartnerDocumentService`, `NotificationInboxService`, `loadScript()`, `authInterceptor`, `apiResponseInterceptor` |
|
|
30
30
|
| **Abstracts** | `BaseTable`, `BaseForm`, `BaseView`, `AbstractEditableView`, `BaseAsync`, `BaseRestService` |
|
|
31
31
|
| **Config** | `SAIL_GUI_CONFIG`, `SailGuiConfig`, `configureRestUrls()` |
|
|
32
|
-
| **Models** | `ApplicationData`, `TableDefinition`, `SiudAction`, `ApplicationMenu`, `ConstantValue`, `UserAccount`, `RestReport`, `ReportParam`, `TrustedDevice`, `PublicPlan`, `PlanPrice`, `PaymentMethod`, `Subscription`, `Invoice`, `CheckoutRequest`/`CheckoutResponse`, `PortalResponse`, `UsageMeter`, `ChargeResult`, `UserPaymentMethod`, `TableAction`, `TableActionParameter`, `ReusableAccount`, `PayoutOnboardingSession`, `BankInfoFormValue`, `Currency`, `Country`, `OtpRequest`/`OtpResponse`, `SignupConsent`, `ConsentState`, `ConsentOption`, `SocialProvider`, `PushPlatform`, `InboxMessage`/`InboxPage`, `PartnerDocument`/`DocumentUploadRequest`, 2FA types, etc. |
|
|
32
|
+
| **Models** | `ApplicationData`, `TableDefinition`, `SiudAction`, `ApplicationMenu`, `ConstantValue`, `UserAccount`, `RestReport`, `ReportParam`, `TrustedDevice`, `PublicPlan`, `PlanPrice`, `PaymentMethod`, `Subscription`, `PlanChangeResult`, `Invoice`, `CheckoutRequest`/`CheckoutResponse`, `PortalResponse`, `UsageMeter`, `ChargeResult`, `UserPaymentMethod`, `TableAction`, `TableActionParameter`, `ReusableAccount`, `PayoutOnboardingSession`, `BankInfoFormValue`, `Currency`, `Country`, `OtpRequest`/`OtpResponse`, `SignupConsent`, `ConsentState`, `ConsentOption`, `SocialProvider`, `PushPlatform`, `InboxMessage`/`InboxPage`, `PartnerDocument`/`DocumentUploadRequest`, 2FA types, etc. |
|
|
33
33
|
| **Decorators** | `@IsSailString()`, `@IsSailNumeric()` (class-validator based) |
|
|
34
34
|
| **Utils** | `titleCase()`, `fromPriceLabel()`, `formatMinor()`, `CurrencyService`, `sailMoney` pipe |
|
|
35
35
|
|
|
@@ -153,6 +153,8 @@ bootstrapApplication(App, {
|
|
|
153
153
|
|
|
154
154
|
> **Auth-loop breaker.** `authInterceptor` carries a built-in circuit breaker: after 5 `401`s on token-bearing keel API calls within 10s it opens for 30s and refuses those requests **locally** (never sent), so a stale/rejected session can't drive the app to hammer the API and the edge/CDN. Only `401` counts — a `403` is a legitimate permission denial, not a session failure. A later success **or accepting a new token** (login) closes it, so a freshly authenticated session is never blocked. No wiring needed beyond registering `authInterceptor`.
|
|
155
155
|
|
|
156
|
+
> **Time zone.** `authInterceptor` also sends `X-Time-Zone` (the browser's IANA zone, read once per page load) on every keel `/api/` and `/public/` request, signed in or not, so keel renders emails, notifications and exports in the user's zone (keel v1.2.118). Timestamps in responses are RFC 3339 UTC; render them with the `date` pipe, without a zone argument.
|
|
157
|
+
|
|
156
158
|
> **Phone-number change (opt-in).** `ProfileEditorComponent` and `MyAccountComponent` gate phone change behind a `phoneChangeEnabled` input (default `false`, since it needs an SMS provider — keel returns 503 without `Notify`). Enable it once your backend has SMS wired: `<sail-my-account [phoneChangeEnabled]="true" />` or `<sail-profile-editor [phoneChangeEnabled]="true" />`.
|
|
157
159
|
|
|
158
160
|
### 5. Create the root component
|
|
@@ -470,7 +472,7 @@ export class PricingPage {
|
|
|
470
472
|
| `createCheckout(req)` | `POST /api/billing/checkout` | `Observable<CheckoutResponse>` |
|
|
471
473
|
| `getSubscription()` | `GET /api/billing/subscription` | `Observable<Subscription>` |
|
|
472
474
|
| `cancelSubscription()` | `POST /api/billing/subscription/cancel` | `Observable<void>` |
|
|
473
|
-
| `changePlan(planId)` | `POST /api/billing/subscription/change` | `Observable<
|
|
475
|
+
| `changePlan(planId)` | `POST /api/billing/subscription/change` | `Observable<PlanChangeResult>` |
|
|
474
476
|
| `setSeats(seats)` | `POST /api/billing/subscription/seats` | `Observable<void>` |
|
|
475
477
|
| `listInvoices()` | `GET /api/billing/invoices` | `Observable<Invoice[]>` |
|
|
476
478
|
| `listPaymentMethods()` | `GET /api/billing/payment-methods` | `Observable<PaymentMethod[]>` |
|
|
@@ -653,7 +655,7 @@ An action with `parameters` (keel v1.2.75 `table_action_parameter`) opens a dial
|
|
|
653
655
|
|
|
654
656
|
### Plan change vs first-time checkout (v1.0.1)
|
|
655
657
|
|
|
656
|
-
First-time checkout uses `<sail-checkout-button>` (provider-hosted). For an **existing** subscription, upgrade/downgrade is a distinct path — `BillingService.changePlan(planId)` routes to keel's `SubscriptionLifecycle.ChangePlan` via an endpoint the app exposes. Wire the picker's selection to whichever path applies:
|
|
658
|
+
First-time checkout uses `<sail-checkout-button>` (provider-hosted). For an **existing** subscription, upgrade/downgrade is a distinct path — `BillingService.changePlan(planId)` routes to keel's `SubscriptionLifecycle.ChangePlan` via an endpoint the app exposes and answers `{ status: 'changed' | 'scheduled' }`: a scheduled change sets `Subscription.scheduledAt` and `scheduledPlanId`, and re-selecting the current plan cancels it. Wire the picker's selection to whichever path applies:
|
|
657
659
|
|
|
658
660
|
```typescript
|
|
659
661
|
onPlanPicked(planId: string) {
|
|
@@ -671,6 +673,14 @@ onPlanPicked(planId: string) {
|
|
|
671
673
|
<sail-dunning-banner [status]="sub()?.status" [providerManaged]="sub()?.providerManaged ?? true"></sail-dunning-banner>
|
|
672
674
|
```
|
|
673
675
|
|
|
676
|
+
### Scheduled change banner (v1.1.41)
|
|
677
|
+
|
|
678
|
+
`<sail-scheduled-change-banner>` shows while `Subscription.scheduledAt` is set: the ending plan, its end date and the plan that follows (captioned from `[plans]`), with a **Keep my plan** button that re-selects the current plan and emits `kept`:
|
|
679
|
+
|
|
680
|
+
```html
|
|
681
|
+
<sail-scheduled-change-banner [subscription]="sub()" [plans]="plans()" (kept)="reload()" />
|
|
682
|
+
```
|
|
683
|
+
|
|
674
684
|
### Compose a full Billing screen
|
|
675
685
|
|
|
676
686
|
A complete screen is the pieces above assembled over `BillingService` — no metadata CRUD (billing data is partner-scoped, read-mostly, money-formatted, with an external-redirect action, which `TableList`/`TableEdit` don't model):
|
|
@@ -678,6 +688,7 @@ A complete screen is the pieces above assembled over `BillingService` — no met
|
|
|
678
688
|
```html
|
|
679
689
|
<sail-dunning-banner [status]="sub()?.status"></sail-dunning-banner>
|
|
680
690
|
<sail-trial-banner [trialEnd]="sub()?.trialEnd"></sail-trial-banner>
|
|
691
|
+
<sail-scheduled-change-banner [subscription]="sub()" [plans]="plans()" (kept)="reload()" />
|
|
681
692
|
<sail-status-chip [value]="sub()?.status"></sail-status-chip>
|
|
682
693
|
<sail-plan-selector [plans]="plans()" [selected]="sub()?.planId" (selectionChange)="onPlanPicked($event)"></sail-plan-selector>
|
|
683
694
|
<sail-price-selector [prices]="selectedPlan()?.prices ?? []" [selected]="offer()?.priceId" (selectionChange)="offer.set($event)"></sail-price-selector>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nauticana/sail",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.42",
|
|
4
4
|
"description": "Metadata-driven Angular component library for CRUD admin frontends backed by the keel Go backend. Tables, forms, navigation, OTP/2FA/social auth, billing, payout onboarding.",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"exports": {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
@if (subscription(); as sub) {
|
|
2
|
+
@if (sub.scheduledAt) {
|
|
3
|
+
<div class="scheduled-change-banner" role="status">
|
|
4
|
+
<span class="scheduled-change-message">
|
|
5
|
+
Your {{ sub.caption || sub.planId }} plan ends on {{ sub.scheduledAt | date: 'mediumDate' }}.
|
|
6
|
+
@if (next()) { {{ next() }} starts then. }
|
|
7
|
+
</span>
|
|
8
|
+
<button matButton type="button" [disabled]="loading()" (click)="keep()">Keep my plan</button>
|
|
9
|
+
@if (errorMessage()) {
|
|
10
|
+
<div class="auth-error" role="alert">{{ errorMessage() }}</div>
|
|
11
|
+
}
|
|
12
|
+
</div>
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, ViewEncapsulation, computed, inject, input, output } from '@angular/core';
|
|
2
|
+
import { DatePipe } from '@angular/common';
|
|
3
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
4
|
+
import { BaseAsync } from '../abstract/base_async';
|
|
5
|
+
import { BillingService } from '../../service/billing.service';
|
|
6
|
+
import { PublicPlan, Subscription } from '../../model/appdata';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Scheduled plan change banner, bound to `Subscription.scheduledAt`: names the
|
|
10
|
+
* ending plan, its end date and the plan that follows, with a Keep my plan
|
|
11
|
+
* action that re-selects the current plan. Renders nothing when no change is scheduled.
|
|
12
|
+
*/
|
|
13
|
+
@Component({
|
|
14
|
+
selector: 'sail-scheduled-change-banner',
|
|
15
|
+
templateUrl: './scheduled_change_banner.html',
|
|
16
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
17
|
+
encapsulation: ViewEncapsulation.None,
|
|
18
|
+
imports: [DatePipe, MatButtonModule],
|
|
19
|
+
})
|
|
20
|
+
export class ScheduledChangeBannerComponent extends BaseAsync {
|
|
21
|
+
private readonly billing = inject(BillingService);
|
|
22
|
+
|
|
23
|
+
readonly subscription = input<Subscription | null | undefined>(undefined);
|
|
24
|
+
/** Catalog plans, to caption the following plan. */
|
|
25
|
+
readonly plans = input<PublicPlan[]>([]);
|
|
26
|
+
/** Emits once the current plan renews again; reload the subscription. */
|
|
27
|
+
readonly kept = output<void>();
|
|
28
|
+
|
|
29
|
+
readonly next = computed(() => {
|
|
30
|
+
const id = this.subscription()?.scheduledPlanId;
|
|
31
|
+
return id ? (this.plans().find((p) => p.id === id)?.caption ?? id) : '';
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
keep(): void {
|
|
35
|
+
const sub = this.subscription();
|
|
36
|
+
if (!sub) return;
|
|
37
|
+
this.run(this.billing.changePlan(sub.planId), () => this.kept.emit(), 'Could not keep your plan. Please try again.');
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -67,6 +67,7 @@ export * from './component/billing/status_chip';
|
|
|
67
67
|
export * from './component/billing/trial_banner';
|
|
68
68
|
export * from './component/billing/seat_selector';
|
|
69
69
|
export * from './component/billing/dunning_banner';
|
|
70
|
+
export * from './component/billing/scheduled_change_banner';
|
|
70
71
|
export * from './component/payout/provider_onboarding';
|
|
71
72
|
export * from './component/payout/bank_info_form';
|
|
72
73
|
export * from './component/agency/agency_status_banner';
|
package/src/model/appdata.ts
CHANGED
|
@@ -406,6 +406,14 @@ export interface Subscription {
|
|
|
406
406
|
seats?: number;
|
|
407
407
|
// False when the provider holds no subscription or customer: hide provider actions (portal, cancel, plan change).
|
|
408
408
|
providerManaged: boolean;
|
|
409
|
+
// The plan that starts at scheduledAt, when this one ends; absent when nothing follows.
|
|
410
|
+
scheduledPlanId?: string;
|
|
411
|
+
scheduledAt?: string;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// keel's plan-change outcome: applied now, or at the period end.
|
|
415
|
+
export interface PlanChangeResult {
|
|
416
|
+
status: 'changed' | 'scheduled';
|
|
409
417
|
}
|
|
410
418
|
|
|
411
419
|
// Mirrors keel `billing.Invoice`; `status` is keel's one-character invoice status code.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { HttpErrorResponse, HttpEvent, HttpEventType, HttpHandlerFn, HttpInterceptorFn, HttpRequest } from '@angular/common/http';
|
|
2
2
|
import { inject, Injector } from '@angular/core';
|
|
3
3
|
import { Observable, catchError, switchMap, tap, throwError } from 'rxjs';
|
|
4
|
-
import { isKeelApiUrl } from './rest_url';
|
|
4
|
+
import { isKeelApiUrl, isKeelUrl } from './rest_url';
|
|
5
5
|
import { BaseAuthService, RefreshSupersededError } from './auth.service';
|
|
6
6
|
|
|
7
7
|
// A 401 on an authed call first tries one refresh-token rotation (keel
|
|
@@ -24,6 +24,9 @@ const OPEN_KEY = 'keel_auth_circuit_until';
|
|
|
24
24
|
* real 401 and from a network failure (status 0). */
|
|
25
25
|
export const AUTH_CIRCUIT_OPEN_CODE = 'sail_auth_circuit_open';
|
|
26
26
|
|
|
27
|
+
// The browser's IANA zone, read once per page load; keel renders emails, notifications and exports in it.
|
|
28
|
+
const TIME_ZONE = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
29
|
+
|
|
27
30
|
export const authInterceptor: HttpInterceptorFn = (req, next) => {
|
|
28
31
|
// Resolved lazily per event to avoid a DI cycle (BaseAuthService → HttpClient
|
|
29
32
|
// → interceptors) and so an open circuit clears the dead session even when it
|
|
@@ -32,6 +35,10 @@ export const authInterceptor: HttpInterceptorFn = (req, next) => {
|
|
|
32
35
|
const auth = () => injector.get(BaseAuthService, null, { optional: true });
|
|
33
36
|
const expireSession = () => auth()?.sessionExpired();
|
|
34
37
|
|
|
38
|
+
if (isKeelUrl(req.url)) {
|
|
39
|
+
req = req.clone({ setHeaders: { 'X-Time-Zone': TIME_ZONE } });
|
|
40
|
+
}
|
|
41
|
+
|
|
35
42
|
const token = localStorage.getItem('jwt');
|
|
36
43
|
const authedApi = !!token && isKeelApiUrl(req.url);
|
|
37
44
|
if (authedApi) {
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
CheckoutResponse,
|
|
8
8
|
Invoice,
|
|
9
9
|
PaymentMethod,
|
|
10
|
+
PlanChangeResult,
|
|
10
11
|
PortalResponse,
|
|
11
12
|
PublicPlan,
|
|
12
13
|
SetupIntentResponse,
|
|
@@ -52,11 +53,12 @@ export class BillingService extends BaseRestService {
|
|
|
52
53
|
|
|
53
54
|
/**
|
|
54
55
|
* Upgrade / downgrade the current subscription to `planId` — distinct from
|
|
55
|
-
* first-time checkout.
|
|
56
|
-
* (
|
|
56
|
+
* first-time checkout. keel applies it now (`changed`) or at the period end
|
|
57
|
+
* (`scheduled`); the current plan resumes renewal. 403 without
|
|
58
|
+
* PARTNER_PLAN_SUBSCRIPTION / CHANGE.
|
|
57
59
|
*/
|
|
58
|
-
changePlan(planId: string): Observable<
|
|
59
|
-
return this.http.post<
|
|
60
|
+
changePlan(planId: string): Observable<PlanChangeResult> {
|
|
61
|
+
return this.http.post<PlanChangeResult>(this.url(RestURL.changePlanURL), { planId });
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
/** Set the seat quantity on the current active/trial subscription. */
|