@sneat/components 0.27.31 → 0.27.33

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription-settings.component.js","sourceRoot":"","sources":["../../../../../../libs/components/src/lib/subscription-settings/subscription-settings.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n InjectionToken,\n OnDestroy,\n computed,\n effect,\n inject,\n input,\n signal,\n untracked,\n} from '@angular/core';\nimport { CurrencyPipe, DatePipe } from '@angular/common';\nimport { Router, RouterLink } from '@angular/router';\nimport { IonButton } from '@ionic/angular/ion-button';\nimport { IonCard } from '@ionic/angular/ion-card';\nimport { IonCardContent } from '@ionic/angular/ion-card-content';\nimport { IonCardHeader } from '@ionic/angular/ion-card-header';\nimport { IonCardTitle } from '@ionic/angular/ion-card-title';\nimport { IonItem } from '@ionic/angular/ion-item';\nimport { IonLabel } from '@ionic/angular/ion-label';\nimport { IonList } from '@ionic/angular/ion-list';\nimport { IonSelect } from '@ionic/angular/ion-select';\nimport { IonSelectOption } from '@ionic/angular/ion-select-option';\n\nexport interface ManagedSubscription {\n accountId: string;\n planId: string;\n label: string;\n period: 'month' | 'year';\n kind: 'personal' | 'organisation';\n spaceID?: string;\n spaceTitle: string;\n status: string;\n paidThroughUTC: string;\n scheduledEndUTC: string;\n firstPaidUTC: string;\n currency?: string;\n regularMinor: number;\n renewalMinor: number;\n discountPercent: number;\n priceConfirmed: boolean;\n launchDiscount: boolean;\n bindingRevision: number;\n canCancel: boolean;\n canTransfer: boolean;\n sessionId?: string;\n}\nexport interface SubscriptionSettingsPort {\n observeIdentity(callback: (id: string | null) => void): () => void;\n load(\n mode: 'live' | 'test',\n after: string,\n ): Promise<{ items: ManagedSubscription[]; next?: string }>;\n read(mode: 'live' | 'test', id: string): Promise<ManagedSubscription>;\n cancel(mode: 'live' | 'test', id: string): Promise<ManagedSubscription>;\n transfer(\n mode: 'live' | 'test',\n item: ManagedSubscription,\n target: string,\n requestId: string,\n ): Promise<void>;\n spaces(): { id: string; title: string }[];\n resume(\n mode: 'live' | 'test',\n item: ManagedSubscription,\n ): { path: string; query: Record<string, string> } | undefined;\n message(error: unknown): string;\n}\nexport const SUBSCRIPTION_SETTINGS =\n new InjectionToken<SubscriptionSettingsPort>('SubscriptionSettings');\n\n@Component({\n selector: 'sneat-subscription-settings',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n RouterLink,\n CurrencyPipe,\n DatePipe,\n IonButton,\n IonCard,\n IonCardContent,\n IonCardHeader,\n IonCardTitle,\n IonItem,\n IonLabel,\n IonList,\n IonSelect,\n IonSelectOption,\n ],\n template: `\n @if (!identity()) {\n <ion-card\n ><ion-card-content\n >Sign in to manage your subscriptions.</ion-card-content\n ></ion-card\n >\n } @else {\n @if (busy()) {\n <p role=\"status\">Checking your subscriptions…</p>\n }\n @if (error()) {\n <ion-card\n ><ion-card-content\n ><p role=\"alert\">{{ error() }}</p>\n <ion-button fill=\"outline\" [disabled]=\"busy()\" (click)=\"refresh()\"\n >Retry</ion-button\n >\n </ion-card-content></ion-card\n >\n }\n @if (!accountId()) {\n @if (!busy() && !error() && !items().length) {\n <p>No subscriptions found.</p>\n }\n <ion-list aria-label=\"Your subscriptions\">\n @for (item of items(); track item.accountId) {\n <ion-item\n button\n detail\n [routerLink]=\"[basePath(), item.accountId]\"\n [queryParams]=\"query()\"\n >\n <ion-label class=\"ion-text-wrap\"\n ><h2>{{ item.label }} · {{ item.spaceTitle }}</h2>\n <p>{{ status(item) }}</p>\n @if (item.priceConfirmed) {\n <p>\n {{ item.renewalMinor / 100 | currency: item.currency }} /\n {{ item.period }}\n </p>\n }\n <p>Reference {{ item.accountId.slice(-8) }}</p>\n </ion-label>\n </ion-item>\n }\n </ion-list>\n @if (next()) {\n <ion-button fill=\"clear\" [disabled]=\"busy()\" (click)=\"loadMore()\"\n >Show more subscriptions</ion-button\n >\n }\n } @else if (selected(); as item) {\n <ion-card>\n <ion-card-header\n ><ion-card-title>{{ item.label }}</ion-card-title></ion-card-header\n >\n <ion-card-content>\n <p>\n Receiving space: <strong>{{ item.spaceTitle }}</strong>\n </p>\n <p>{{ status(item) }}</p>\n @if (item.priceConfirmed) {\n <p>\n Regular price:\n {{ item.regularMinor / 100 | currency: item.currency }} /\n {{ item.period }}\n </p>\n @if (item.discountPercent) {\n <p>\n {{\n item.launchDiscount\n ? 'Launch discount'\n : 'Renewal discount'\n }}: {{ item.discountPercent }}%\n </p>\n }\n <p>\n <strong\n >{{\n item.scheduledEndUTC.startsWith('0001-')\n ? 'Renewal'\n : 'Price per period'\n }}: {{ item.renewalMinor / 100 | currency: item.currency }} /\n {{ item.period }}</strong\n >\n </p>\n } @else {\n <p>Price requires a payment status check.</p>\n }\n @if (!item.paidThroughUTC.startsWith('0001-')) {\n <p>Paid through {{ item.paidThroughUTC | date: 'mediumDate' }}</p>\n }\n @if (!item.scheduledEndUTC.startsWith('0001-')) {\n <p>\n Renewal cancelled. Access ends\n {{ item.scheduledEndUTC | date: 'mediumDate' }}.\n </p>\n }\n @if (resume(item); as continuation) {\n <ion-button\n [routerLink]=\"continuation.path\"\n [queryParams]=\"continuation.query\"\n >{{\n item.status === 'setup_required'\n ? 'Finish subscription setup'\n : 'Check payment status'\n }}</ion-button\n >\n }\n @if (!transferView() && item.canTransfer) {\n <ion-button\n fill=\"outline\"\n [routerLink]=\"[basePath(), item.accountId, 'transfer']\"\n [queryParams]=\"transferQuery()\"\n [disabled]=\"busy()\"\n >Transfer paid access</ion-button\n >\n }\n @if (!transferView() && item.canCancel && !confirmCancel()) {\n <ion-button\n fill=\"clear\"\n color=\"danger\"\n [disabled]=\"busy()\"\n (click)=\"confirmCancel.set(true)\"\n >Cancel renewal</ion-button\n >\n }\n </ion-card-content>\n </ion-card>\n @if (confirmCancel()) {\n <ion-card aria-label=\"Confirm cancellation\"\n ><ion-card-content>\n <p>\n Cancel renewal for {{ item.label }} in {{ item.spaceTitle }}?\n </p>\n <p>\n Paid access remains until\n {{ item.paidThroughUTC | date: 'mediumDate' }}. Your other\n subscriptions stay active.\n </p>\n <ion-button color=\"danger\" [disabled]=\"busy()\" (click)=\"cancel()\"\n >Confirm cancellation at period end</ion-button\n >\n <ion-button\n fill=\"clear\"\n [disabled]=\"busy()\"\n (click)=\"confirmCancel.set(false)\"\n >Keep subscription</ion-button\n >\n </ion-card-content></ion-card\n >\n }\n @if (transferView()) {\n <ion-card aria-label=\"Transfer paid access\"\n ><ion-card-header\n ><ion-card-title\n >Transfer paid access</ion-card-title\n ></ion-card-header\n >\n <ion-card-content>\n <p>\n Projects stay in {{ item.spaceTitle }}. That space loses this\n subscription's paid access after transfer; normal expiry\n restrictions apply.\n </p>\n <p>\n Billing dates, discount and the original introductory period\n stay unchanged. Separate prepaid capacity and SSO add-ons stay\n in their current space.\n </p>\n <p>\n Transfers with consumed current-period allowance require\n settlement first; transferring never grants a fresh allowance.\n </p>\n @if (!item.canTransfer) {\n <p role=\"alert\">\n This subscription cannot currently be transferred. Check its\n payment status first.\n </p>\n }\n <ion-select\n label=\"Receiving business space\"\n interface=\"popover\"\n [value]=\"target()\"\n [disabled]=\"busy() || !item.canTransfer\"\n (ionChange)=\"target.set($event.detail.value)\"\n >\n @for (space of eligibleSpaces(); track space.id) {\n <ion-select-option [value]=\"space.id\">{{\n space.title\n }}</ion-select-option>\n }\n </ion-select>\n @if (!eligibleSpaces().length) {\n <p>No other eligible business space is available.</p>\n }\n <ion-button\n [disabled]=\"\n busy() || !target() || !item.canTransfer || !validRequest()\n \"\n (click)=\"transfer()\"\n >Confirm transfer of paid access</ion-button\n >\n <ion-button\n fill=\"clear\"\n [routerLink]=\"[basePath(), item.accountId]\"\n [queryParams]=\"query()\"\n [disabled]=\"busy()\"\n >Cancel</ion-button\n >\n </ion-card-content>\n </ion-card>\n }\n }\n }\n `,\n})\nexport class SubscriptionSettingsComponent implements OnDestroy {\n private readonly port = inject(SUBSCRIPTION_SETTINGS, { optional: true });\n private readonly router = inject(Router);\n readonly accountId = input('');\n readonly mode = input<'live' | 'test'>('live');\n readonly transferView = input(false);\n readonly requestId = input('');\n readonly basePath = input('/subscriptions');\n protected readonly identity = signal<string | null>(null);\n protected readonly items = signal<ManagedSubscription[]>([]);\n protected readonly selected = signal<ManagedSubscription | null>(null);\n protected readonly busy = signal(false);\n protected readonly error = signal('');\n protected readonly next = signal('');\n protected readonly target = signal('');\n protected readonly confirmCancel = signal(false);\n protected readonly query = computed(() =>\n this.mode() === 'test' ? { checkout: 'test' } : {},\n );\n protected readonly transferQuery = computed(() => ({\n ...this.query(),\n requestId: `${this.selected()?.bindingRevision ?? 0}-${crypto.randomUUID()}`,\n }));\n protected readonly validRequest = computed(() =>\n /^[A-Za-z0-9_-]{1,128}$/.test(this.requestId()),\n );\n protected readonly eligibleSpaces = computed(() =>\n (this.port?.spaces() ?? []).filter(\n (s) => s.id !== this.selected()?.spaceID,\n ),\n );\n private generation = 0;\n private key = '';\n private readonly stop = this.port?.observeIdentity((id) =>\n this.identity.set(id),\n );\n constructor() {\n effect(() => {\n const key = [\n this.identity(),\n this.mode(),\n this.accountId(),\n this.transferView(),\n this.requestId(),\n ].join('|');\n if (key === this.key) return;\n this.key = key;\n this.generation++;\n this.items.set([]);\n this.selected.set(null);\n this.next.set('');\n this.error.set('');\n this.busy.set(false);\n this.target.set('');\n this.confirmCancel.set(false);\n if (this.identity())\n untracked(() => {\n void this.read(false);\n });\n });\n }\n protected status(item: ManagedSubscription): string {\n return (\n (\n {\n active: 'Active',\n ended: 'Ended',\n refunded: 'Refunded',\n cancellation_scheduled: 'Cancellation scheduled',\n setup_required: 'Payment received · Finish setup',\n check_status: 'Payment status check required',\n } as Record<string, string>\n )[item.status] ?? 'Status unavailable'\n );\n }\n protected resume(item: ManagedSubscription) {\n return this.port?.resume(this.mode(), item);\n }\n private async read(more: boolean): Promise<void> {\n if (!this.port || !this.identity() || this.busy()) return;\n const generation = this.generation;\n this.busy.set(true);\n this.error.set('');\n try {\n if (this.accountId()) {\n const item = await this.port.read(this.mode(), this.accountId());\n if (generation === this.generation) this.selected.set(item);\n } else {\n const page = await this.port.load(this.mode(), more ? this.next() : '');\n if (generation === this.generation) {\n this.items.set([\n ...new Map(\n [...(more ? this.items() : []), ...page.items].map((i) => [\n i.accountId,\n i,\n ]),\n ).values(),\n ]);\n this.next.set(page.next ?? '');\n }\n }\n } catch (error) {\n if (generation === this.generation)\n this.error.set(this.port.message(error));\n } finally {\n if (generation === this.generation) this.busy.set(false);\n }\n }\n refresh(): void {\n if (!this.busy()) void this.read(false);\n }\n protected loadMore(): void {\n void this.read(true);\n }\n protected async cancel(): Promise<void> {\n const item = this.selected();\n if (!item || !this.port || this.busy() || !this.confirmCancel()) return;\n const generation = this.generation;\n this.busy.set(true);\n this.error.set('');\n try {\n const result = await this.port.cancel(this.mode(), item.accountId);\n if (generation === this.generation) {\n this.selected.set(result);\n this.confirmCancel.set(false);\n }\n } catch (error) {\n if (generation === this.generation)\n this.error.set(this.port.message(error));\n } finally {\n if (generation === this.generation) this.busy.set(false);\n }\n }\n protected async transfer(): Promise<void> {\n const item = this.selected();\n if (\n !item ||\n !this.port ||\n this.busy() ||\n !this.validRequest() ||\n !this.eligibleSpaces().some((s) => s.id === this.target()) ||\n !item.canTransfer\n )\n return;\n const generation = this.generation;\n this.busy.set(true);\n this.error.set('');\n try {\n await this.port.transfer(\n this.mode(),\n item,\n this.target(),\n this.requestId(),\n );\n if (generation === this.generation)\n await this.router.navigate([this.basePath(), item.accountId], {\n queryParams: this.query(),\n replaceUrl: true,\n });\n } catch (error) {\n if (generation === this.generation)\n this.error.set(this.port.message(error));\n } finally {\n if (generation === this.generation) this.busy.set(false);\n }\n }\n ngOnDestroy(): void {\n this.generation++;\n this.stop?.();\n }\n}\n"]}
package/index.d.ts CHANGED
@@ -13,3 +13,4 @@ export * from './lib/date-input/date-input.component';
13
13
  export * from './lib/user-country/user-country.component';
14
14
  export * from './lib/card-list/sneat-card-list.component';
15
15
  export * from './lib/subscription-recovery/subscription-recovery.component';
16
+ export * from './lib/subscription-settings/subscription-settings.component';
@@ -44,6 +44,7 @@ export declare class CountriesLoaderService {
44
44
  * Automatically loads country data if not yet loaded.
45
45
  */
46
46
  getUnknownCountry(): Promise<ICountry>;
47
+ private loadedCountries;
47
48
  /**
48
49
  * Gets countries filtered by geo region.
49
50
  * Automatically loads country data if not yet loaded.
@@ -4,6 +4,7 @@ export type SubscriptionRecoveryMode = 'live' | 'test';
4
4
  export interface SubscriptionRecoveryItem {
5
5
  id: string;
6
6
  label: string;
7
+ description?: string;
7
8
  action: string;
8
9
  path: string;
9
10
  query: Record<string, string>;
@@ -0,0 +1,96 @@
1
+ import { InjectionToken, OnDestroy } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ export interface ManagedSubscription {
4
+ accountId: string;
5
+ planId: string;
6
+ label: string;
7
+ period: 'month' | 'year';
8
+ kind: 'personal' | 'organisation';
9
+ spaceID?: string;
10
+ spaceTitle: string;
11
+ status: string;
12
+ paidThroughUTC: string;
13
+ scheduledEndUTC: string;
14
+ firstPaidUTC: string;
15
+ currency?: string;
16
+ regularMinor: number;
17
+ renewalMinor: number;
18
+ discountPercent: number;
19
+ priceConfirmed: boolean;
20
+ launchDiscount: boolean;
21
+ bindingRevision: number;
22
+ canCancel: boolean;
23
+ canTransfer: boolean;
24
+ sessionId?: string;
25
+ }
26
+ export interface SubscriptionSettingsPort {
27
+ observeIdentity(callback: (id: string | null) => void): () => void;
28
+ load(mode: 'live' | 'test', after: string): Promise<{
29
+ items: ManagedSubscription[];
30
+ next?: string;
31
+ }>;
32
+ read(mode: 'live' | 'test', id: string): Promise<ManagedSubscription>;
33
+ cancel(mode: 'live' | 'test', id: string): Promise<ManagedSubscription>;
34
+ transfer(mode: 'live' | 'test', item: ManagedSubscription, target: string, requestId: string): Promise<void>;
35
+ spaces(): {
36
+ id: string;
37
+ title: string;
38
+ }[];
39
+ resume(mode: 'live' | 'test', item: ManagedSubscription): {
40
+ path: string;
41
+ query: Record<string, string>;
42
+ } | undefined;
43
+ message(error: unknown): string;
44
+ }
45
+ export declare const SUBSCRIPTION_SETTINGS: InjectionToken<SubscriptionSettingsPort>;
46
+ export declare class SubscriptionSettingsComponent implements OnDestroy {
47
+ private readonly port;
48
+ private readonly router;
49
+ readonly accountId: import("@angular/core").InputSignal<string>;
50
+ readonly mode: import("@angular/core").InputSignal<"live" | "test">;
51
+ readonly transferView: import("@angular/core").InputSignal<boolean>;
52
+ readonly requestId: import("@angular/core").InputSignal<string>;
53
+ readonly basePath: import("@angular/core").InputSignal<string>;
54
+ protected readonly identity: import("@angular/core").WritableSignal<string | null>;
55
+ protected readonly items: import("@angular/core").WritableSignal<ManagedSubscription[]>;
56
+ protected readonly selected: import("@angular/core").WritableSignal<ManagedSubscription | null>;
57
+ protected readonly busy: import("@angular/core").WritableSignal<boolean>;
58
+ protected readonly error: import("@angular/core").WritableSignal<string>;
59
+ protected readonly next: import("@angular/core").WritableSignal<string>;
60
+ protected readonly target: import("@angular/core").WritableSignal<string>;
61
+ protected readonly confirmCancel: import("@angular/core").WritableSignal<boolean>;
62
+ protected readonly query: import("@angular/core").Signal<{
63
+ checkout: string;
64
+ } | {
65
+ checkout?: undefined;
66
+ }>;
67
+ protected readonly transferQuery: import("@angular/core").Signal<{
68
+ requestId: string;
69
+ checkout: string;
70
+ } | {
71
+ requestId: string;
72
+ checkout?: undefined;
73
+ }>;
74
+ protected readonly validRequest: import("@angular/core").Signal<boolean>;
75
+ protected readonly eligibleSpaces: import("@angular/core").Signal<{
76
+ id: string;
77
+ title: string;
78
+ }[]>;
79
+ private generation;
80
+ private key;
81
+ private readonly stop;
82
+ constructor();
83
+ protected status(item: ManagedSubscription): string;
84
+ protected resume(item: ManagedSubscription): {
85
+ path: string;
86
+ query: Record<string, string>;
87
+ } | undefined;
88
+ private read;
89
+ refresh(): void;
90
+ protected loadMore(): void;
91
+ protected cancel(): Promise<void>;
92
+ protected transfer(): Promise<void>;
93
+ ngOnDestroy(): void;
94
+ static ɵfac: i0.ɵɵFactoryDeclaration<SubscriptionSettingsComponent, never>;
95
+ static ɵcmp: i0.ɵɵComponentDeclaration<SubscriptionSettingsComponent, "sneat-subscription-settings", never, { "accountId": { "alias": "accountId"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "transferView": { "alias": "transferView"; "required": false; "isSignal": true; }; "requestId": { "alias": "requestId"; "required": false; "isSignal": true; }; "basePath": { "alias": "basePath"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
96
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sneat/components",
3
- "version": "0.27.31",
3
+ "version": "0.27.33",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },