@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.
- package/esm2022/index.js +1 -0
- package/esm2022/index.js.map +1 -1
- package/esm2022/lib/country-selector/countries-loader.service.js +9 -7
- package/esm2022/lib/country-selector/countries-loader.service.js.map +1 -1
- package/esm2022/lib/country-selector/country-selector.component.js +4 -3
- package/esm2022/lib/country-selector/country-selector.component.js.map +1 -1
- package/esm2022/lib/subscription-recovery/subscription-recovery.component.js +12 -6
- package/esm2022/lib/subscription-recovery/subscription-recovery.component.js.map +1 -1
- package/esm2022/lib/subscription-settings/subscription-settings.component.js +653 -0
- package/esm2022/lib/subscription-settings/subscription-settings.component.js.map +1 -0
- package/index.d.ts +1 -0
- package/lib/country-selector/countries-loader.service.d.ts +1 -0
- package/lib/subscription-recovery/subscription-recovery.component.d.ts +1 -0
- package/lib/subscription-settings/subscription-settings.component.d.ts +96 -0
- package/package.json +1 -1
- package/tsconfig.lib.prod.tsbuildinfo +1 -1
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, InjectionToken, computed, effect, inject, input, signal, untracked, } from '@angular/core';
|
|
2
|
+
import { CurrencyPipe, DatePipe } from '@angular/common';
|
|
3
|
+
import { Router, RouterLink } from '@angular/router';
|
|
4
|
+
import { IonButton } from '@ionic/angular/ion-button';
|
|
5
|
+
import { IonCard } from '@ionic/angular/ion-card';
|
|
6
|
+
import { IonCardContent } from '@ionic/angular/ion-card-content';
|
|
7
|
+
import { IonCardHeader } from '@ionic/angular/ion-card-header';
|
|
8
|
+
import { IonCardTitle } from '@ionic/angular/ion-card-title';
|
|
9
|
+
import { IonItem } from '@ionic/angular/ion-item';
|
|
10
|
+
import { IonLabel } from '@ionic/angular/ion-label';
|
|
11
|
+
import { IonList } from '@ionic/angular/ion-list';
|
|
12
|
+
import { IonSelect } from '@ionic/angular/ion-select';
|
|
13
|
+
import { IonSelectOption } from '@ionic/angular/ion-select-option';
|
|
14
|
+
import * as i0 from "@angular/core";
|
|
15
|
+
export const SUBSCRIPTION_SETTINGS = new InjectionToken('SubscriptionSettings');
|
|
16
|
+
export class SubscriptionSettingsComponent {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.port = inject(SUBSCRIPTION_SETTINGS, { optional: true });
|
|
19
|
+
this.router = inject(Router);
|
|
20
|
+
this.accountId = input('', /* @ts-ignore */
|
|
21
|
+
...(ngDevMode ? [{ debugName: "accountId" }] : /* istanbul ignore next */ []));
|
|
22
|
+
this.mode = input('live', /* @ts-ignore */
|
|
23
|
+
...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
24
|
+
this.transferView = input(false, /* @ts-ignore */
|
|
25
|
+
...(ngDevMode ? [{ debugName: "transferView" }] : /* istanbul ignore next */ []));
|
|
26
|
+
this.requestId = input('', /* @ts-ignore */
|
|
27
|
+
...(ngDevMode ? [{ debugName: "requestId" }] : /* istanbul ignore next */ []));
|
|
28
|
+
this.basePath = input('/subscriptions', /* @ts-ignore */
|
|
29
|
+
...(ngDevMode ? [{ debugName: "basePath" }] : /* istanbul ignore next */ []));
|
|
30
|
+
this.identity = signal(null, /* @ts-ignore */
|
|
31
|
+
...(ngDevMode ? [{ debugName: "identity" }] : /* istanbul ignore next */ []));
|
|
32
|
+
this.items = signal([], /* @ts-ignore */
|
|
33
|
+
...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
34
|
+
this.selected = signal(null, /* @ts-ignore */
|
|
35
|
+
...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
|
|
36
|
+
this.busy = signal(false, /* @ts-ignore */
|
|
37
|
+
...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
|
|
38
|
+
this.error = signal('', /* @ts-ignore */
|
|
39
|
+
...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
40
|
+
this.next = signal('', /* @ts-ignore */
|
|
41
|
+
...(ngDevMode ? [{ debugName: "next" }] : /* istanbul ignore next */ []));
|
|
42
|
+
this.target = signal('', /* @ts-ignore */
|
|
43
|
+
...(ngDevMode ? [{ debugName: "target" }] : /* istanbul ignore next */ []));
|
|
44
|
+
this.confirmCancel = signal(false, /* @ts-ignore */
|
|
45
|
+
...(ngDevMode ? [{ debugName: "confirmCancel" }] : /* istanbul ignore next */ []));
|
|
46
|
+
this.query = computed(() => this.mode() === 'test' ? { checkout: 'test' } : {}, /* @ts-ignore */
|
|
47
|
+
...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
|
|
48
|
+
this.transferQuery = computed(() => ({
|
|
49
|
+
...this.query(),
|
|
50
|
+
requestId: `${this.selected()?.bindingRevision ?? 0}-${crypto.randomUUID()}`,
|
|
51
|
+
}), /* @ts-ignore */
|
|
52
|
+
...(ngDevMode ? [{ debugName: "transferQuery" }] : /* istanbul ignore next */ []));
|
|
53
|
+
this.validRequest = computed(() => /^[A-Za-z0-9_-]{1,128}$/.test(this.requestId()), /* @ts-ignore */
|
|
54
|
+
...(ngDevMode ? [{ debugName: "validRequest" }] : /* istanbul ignore next */ []));
|
|
55
|
+
this.eligibleSpaces = computed(() => (this.port?.spaces() ?? []).filter((s) => s.id !== this.selected()?.spaceID), /* @ts-ignore */
|
|
56
|
+
...(ngDevMode ? [{ debugName: "eligibleSpaces" }] : /* istanbul ignore next */ []));
|
|
57
|
+
this.generation = 0;
|
|
58
|
+
this.key = '';
|
|
59
|
+
this.stop = this.port?.observeIdentity((id) => this.identity.set(id));
|
|
60
|
+
effect(() => {
|
|
61
|
+
const key = [
|
|
62
|
+
this.identity(),
|
|
63
|
+
this.mode(),
|
|
64
|
+
this.accountId(),
|
|
65
|
+
this.transferView(),
|
|
66
|
+
this.requestId(),
|
|
67
|
+
].join('|');
|
|
68
|
+
if (key === this.key)
|
|
69
|
+
return;
|
|
70
|
+
this.key = key;
|
|
71
|
+
this.generation++;
|
|
72
|
+
this.items.set([]);
|
|
73
|
+
this.selected.set(null);
|
|
74
|
+
this.next.set('');
|
|
75
|
+
this.error.set('');
|
|
76
|
+
this.busy.set(false);
|
|
77
|
+
this.target.set('');
|
|
78
|
+
this.confirmCancel.set(false);
|
|
79
|
+
if (this.identity())
|
|
80
|
+
untracked(() => {
|
|
81
|
+
void this.read(false);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
status(item) {
|
|
86
|
+
return ({
|
|
87
|
+
active: 'Active',
|
|
88
|
+
ended: 'Ended',
|
|
89
|
+
refunded: 'Refunded',
|
|
90
|
+
cancellation_scheduled: 'Cancellation scheduled',
|
|
91
|
+
setup_required: 'Payment received · Finish setup',
|
|
92
|
+
check_status: 'Payment status check required',
|
|
93
|
+
}[item.status] ?? 'Status unavailable');
|
|
94
|
+
}
|
|
95
|
+
resume(item) {
|
|
96
|
+
return this.port?.resume(this.mode(), item);
|
|
97
|
+
}
|
|
98
|
+
async read(more) {
|
|
99
|
+
if (!this.port || !this.identity() || this.busy())
|
|
100
|
+
return;
|
|
101
|
+
const generation = this.generation;
|
|
102
|
+
this.busy.set(true);
|
|
103
|
+
this.error.set('');
|
|
104
|
+
try {
|
|
105
|
+
if (this.accountId()) {
|
|
106
|
+
const item = await this.port.read(this.mode(), this.accountId());
|
|
107
|
+
if (generation === this.generation)
|
|
108
|
+
this.selected.set(item);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
const page = await this.port.load(this.mode(), more ? this.next() : '');
|
|
112
|
+
if (generation === this.generation) {
|
|
113
|
+
this.items.set([
|
|
114
|
+
...new Map([...(more ? this.items() : []), ...page.items].map((i) => [
|
|
115
|
+
i.accountId,
|
|
116
|
+
i,
|
|
117
|
+
])).values(),
|
|
118
|
+
]);
|
|
119
|
+
this.next.set(page.next ?? '');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (generation === this.generation)
|
|
125
|
+
this.error.set(this.port.message(error));
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
if (generation === this.generation)
|
|
129
|
+
this.busy.set(false);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
refresh() {
|
|
133
|
+
if (!this.busy())
|
|
134
|
+
void this.read(false);
|
|
135
|
+
}
|
|
136
|
+
loadMore() {
|
|
137
|
+
void this.read(true);
|
|
138
|
+
}
|
|
139
|
+
async cancel() {
|
|
140
|
+
const item = this.selected();
|
|
141
|
+
if (!item || !this.port || this.busy() || !this.confirmCancel())
|
|
142
|
+
return;
|
|
143
|
+
const generation = this.generation;
|
|
144
|
+
this.busy.set(true);
|
|
145
|
+
this.error.set('');
|
|
146
|
+
try {
|
|
147
|
+
const result = await this.port.cancel(this.mode(), item.accountId);
|
|
148
|
+
if (generation === this.generation) {
|
|
149
|
+
this.selected.set(result);
|
|
150
|
+
this.confirmCancel.set(false);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (generation === this.generation)
|
|
155
|
+
this.error.set(this.port.message(error));
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
if (generation === this.generation)
|
|
159
|
+
this.busy.set(false);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async transfer() {
|
|
163
|
+
const item = this.selected();
|
|
164
|
+
if (!item ||
|
|
165
|
+
!this.port ||
|
|
166
|
+
this.busy() ||
|
|
167
|
+
!this.validRequest() ||
|
|
168
|
+
!this.eligibleSpaces().some((s) => s.id === this.target()) ||
|
|
169
|
+
!item.canTransfer)
|
|
170
|
+
return;
|
|
171
|
+
const generation = this.generation;
|
|
172
|
+
this.busy.set(true);
|
|
173
|
+
this.error.set('');
|
|
174
|
+
try {
|
|
175
|
+
await this.port.transfer(this.mode(), item, this.target(), this.requestId());
|
|
176
|
+
if (generation === this.generation)
|
|
177
|
+
await this.router.navigate([this.basePath(), item.accountId], {
|
|
178
|
+
queryParams: this.query(),
|
|
179
|
+
replaceUrl: true,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
if (generation === this.generation)
|
|
184
|
+
this.error.set(this.port.message(error));
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
if (generation === this.generation)
|
|
188
|
+
this.busy.set(false);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
ngOnDestroy() {
|
|
192
|
+
this.generation++;
|
|
193
|
+
this.stop?.();
|
|
194
|
+
}
|
|
195
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SubscriptionSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
196
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SubscriptionSettingsComponent, isStandalone: true, selector: "sneat-subscription-settings", inputs: { accountId: { classPropertyName: "accountId", publicName: "accountId", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, transferView: { classPropertyName: "transferView", publicName: "transferView", isSignal: true, isRequired: false, transformFunction: null }, requestId: { classPropertyName: "requestId", publicName: "requestId", isSignal: true, isRequired: false, transformFunction: null }, basePath: { classPropertyName: "basePath", publicName: "basePath", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
197
|
+
@if (!identity()) {
|
|
198
|
+
<ion-card
|
|
199
|
+
><ion-card-content
|
|
200
|
+
>Sign in to manage your subscriptions.</ion-card-content
|
|
201
|
+
></ion-card
|
|
202
|
+
>
|
|
203
|
+
} @else {
|
|
204
|
+
@if (busy()) {
|
|
205
|
+
<p role="status">Checking your subscriptions…</p>
|
|
206
|
+
}
|
|
207
|
+
@if (error()) {
|
|
208
|
+
<ion-card
|
|
209
|
+
><ion-card-content
|
|
210
|
+
><p role="alert">{{ error() }}</p>
|
|
211
|
+
<ion-button fill="outline" [disabled]="busy()" (click)="refresh()"
|
|
212
|
+
>Retry</ion-button
|
|
213
|
+
>
|
|
214
|
+
</ion-card-content></ion-card
|
|
215
|
+
>
|
|
216
|
+
}
|
|
217
|
+
@if (!accountId()) {
|
|
218
|
+
@if (!busy() && !error() && !items().length) {
|
|
219
|
+
<p>No subscriptions found.</p>
|
|
220
|
+
}
|
|
221
|
+
<ion-list aria-label="Your subscriptions">
|
|
222
|
+
@for (item of items(); track item.accountId) {
|
|
223
|
+
<ion-item
|
|
224
|
+
button
|
|
225
|
+
detail
|
|
226
|
+
[routerLink]="[basePath(), item.accountId]"
|
|
227
|
+
[queryParams]="query()"
|
|
228
|
+
>
|
|
229
|
+
<ion-label class="ion-text-wrap"
|
|
230
|
+
><h2>{{ item.label }} · {{ item.spaceTitle }}</h2>
|
|
231
|
+
<p>{{ status(item) }}</p>
|
|
232
|
+
@if (item.priceConfirmed) {
|
|
233
|
+
<p>
|
|
234
|
+
{{ item.renewalMinor / 100 | currency: item.currency }} /
|
|
235
|
+
{{ item.period }}
|
|
236
|
+
</p>
|
|
237
|
+
}
|
|
238
|
+
<p>Reference {{ item.accountId.slice(-8) }}</p>
|
|
239
|
+
</ion-label>
|
|
240
|
+
</ion-item>
|
|
241
|
+
}
|
|
242
|
+
</ion-list>
|
|
243
|
+
@if (next()) {
|
|
244
|
+
<ion-button fill="clear" [disabled]="busy()" (click)="loadMore()"
|
|
245
|
+
>Show more subscriptions</ion-button
|
|
246
|
+
>
|
|
247
|
+
}
|
|
248
|
+
} @else if (selected(); as item) {
|
|
249
|
+
<ion-card>
|
|
250
|
+
<ion-card-header
|
|
251
|
+
><ion-card-title>{{ item.label }}</ion-card-title></ion-card-header
|
|
252
|
+
>
|
|
253
|
+
<ion-card-content>
|
|
254
|
+
<p>
|
|
255
|
+
Receiving space: <strong>{{ item.spaceTitle }}</strong>
|
|
256
|
+
</p>
|
|
257
|
+
<p>{{ status(item) }}</p>
|
|
258
|
+
@if (item.priceConfirmed) {
|
|
259
|
+
<p>
|
|
260
|
+
Regular price:
|
|
261
|
+
{{ item.regularMinor / 100 | currency: item.currency }} /
|
|
262
|
+
{{ item.period }}
|
|
263
|
+
</p>
|
|
264
|
+
@if (item.discountPercent) {
|
|
265
|
+
<p>
|
|
266
|
+
{{
|
|
267
|
+
item.launchDiscount
|
|
268
|
+
? 'Launch discount'
|
|
269
|
+
: 'Renewal discount'
|
|
270
|
+
}}: {{ item.discountPercent }}%
|
|
271
|
+
</p>
|
|
272
|
+
}
|
|
273
|
+
<p>
|
|
274
|
+
<strong
|
|
275
|
+
>{{
|
|
276
|
+
item.scheduledEndUTC.startsWith('0001-')
|
|
277
|
+
? 'Renewal'
|
|
278
|
+
: 'Price per period'
|
|
279
|
+
}}: {{ item.renewalMinor / 100 | currency: item.currency }} /
|
|
280
|
+
{{ item.period }}</strong
|
|
281
|
+
>
|
|
282
|
+
</p>
|
|
283
|
+
} @else {
|
|
284
|
+
<p>Price requires a payment status check.</p>
|
|
285
|
+
}
|
|
286
|
+
@if (!item.paidThroughUTC.startsWith('0001-')) {
|
|
287
|
+
<p>Paid through {{ item.paidThroughUTC | date: 'mediumDate' }}</p>
|
|
288
|
+
}
|
|
289
|
+
@if (!item.scheduledEndUTC.startsWith('0001-')) {
|
|
290
|
+
<p>
|
|
291
|
+
Renewal cancelled. Access ends
|
|
292
|
+
{{ item.scheduledEndUTC | date: 'mediumDate' }}.
|
|
293
|
+
</p>
|
|
294
|
+
}
|
|
295
|
+
@if (resume(item); as continuation) {
|
|
296
|
+
<ion-button
|
|
297
|
+
[routerLink]="continuation.path"
|
|
298
|
+
[queryParams]="continuation.query"
|
|
299
|
+
>{{
|
|
300
|
+
item.status === 'setup_required'
|
|
301
|
+
? 'Finish subscription setup'
|
|
302
|
+
: 'Check payment status'
|
|
303
|
+
}}</ion-button
|
|
304
|
+
>
|
|
305
|
+
}
|
|
306
|
+
@if (!transferView() && item.canTransfer) {
|
|
307
|
+
<ion-button
|
|
308
|
+
fill="outline"
|
|
309
|
+
[routerLink]="[basePath(), item.accountId, 'transfer']"
|
|
310
|
+
[queryParams]="transferQuery()"
|
|
311
|
+
[disabled]="busy()"
|
|
312
|
+
>Transfer paid access</ion-button
|
|
313
|
+
>
|
|
314
|
+
}
|
|
315
|
+
@if (!transferView() && item.canCancel && !confirmCancel()) {
|
|
316
|
+
<ion-button
|
|
317
|
+
fill="clear"
|
|
318
|
+
color="danger"
|
|
319
|
+
[disabled]="busy()"
|
|
320
|
+
(click)="confirmCancel.set(true)"
|
|
321
|
+
>Cancel renewal</ion-button
|
|
322
|
+
>
|
|
323
|
+
}
|
|
324
|
+
</ion-card-content>
|
|
325
|
+
</ion-card>
|
|
326
|
+
@if (confirmCancel()) {
|
|
327
|
+
<ion-card aria-label="Confirm cancellation"
|
|
328
|
+
><ion-card-content>
|
|
329
|
+
<p>
|
|
330
|
+
Cancel renewal for {{ item.label }} in {{ item.spaceTitle }}?
|
|
331
|
+
</p>
|
|
332
|
+
<p>
|
|
333
|
+
Paid access remains until
|
|
334
|
+
{{ item.paidThroughUTC | date: 'mediumDate' }}. Your other
|
|
335
|
+
subscriptions stay active.
|
|
336
|
+
</p>
|
|
337
|
+
<ion-button color="danger" [disabled]="busy()" (click)="cancel()"
|
|
338
|
+
>Confirm cancellation at period end</ion-button
|
|
339
|
+
>
|
|
340
|
+
<ion-button
|
|
341
|
+
fill="clear"
|
|
342
|
+
[disabled]="busy()"
|
|
343
|
+
(click)="confirmCancel.set(false)"
|
|
344
|
+
>Keep subscription</ion-button
|
|
345
|
+
>
|
|
346
|
+
</ion-card-content></ion-card
|
|
347
|
+
>
|
|
348
|
+
}
|
|
349
|
+
@if (transferView()) {
|
|
350
|
+
<ion-card aria-label="Transfer paid access"
|
|
351
|
+
><ion-card-header
|
|
352
|
+
><ion-card-title
|
|
353
|
+
>Transfer paid access</ion-card-title
|
|
354
|
+
></ion-card-header
|
|
355
|
+
>
|
|
356
|
+
<ion-card-content>
|
|
357
|
+
<p>
|
|
358
|
+
Projects stay in {{ item.spaceTitle }}. That space loses this
|
|
359
|
+
subscription's paid access after transfer; normal expiry
|
|
360
|
+
restrictions apply.
|
|
361
|
+
</p>
|
|
362
|
+
<p>
|
|
363
|
+
Billing dates, discount and the original introductory period
|
|
364
|
+
stay unchanged. Separate prepaid capacity and SSO add-ons stay
|
|
365
|
+
in their current space.
|
|
366
|
+
</p>
|
|
367
|
+
<p>
|
|
368
|
+
Transfers with consumed current-period allowance require
|
|
369
|
+
settlement first; transferring never grants a fresh allowance.
|
|
370
|
+
</p>
|
|
371
|
+
@if (!item.canTransfer) {
|
|
372
|
+
<p role="alert">
|
|
373
|
+
This subscription cannot currently be transferred. Check its
|
|
374
|
+
payment status first.
|
|
375
|
+
</p>
|
|
376
|
+
}
|
|
377
|
+
<ion-select
|
|
378
|
+
label="Receiving business space"
|
|
379
|
+
interface="popover"
|
|
380
|
+
[value]="target()"
|
|
381
|
+
[disabled]="busy() || !item.canTransfer"
|
|
382
|
+
(ionChange)="target.set($event.detail.value)"
|
|
383
|
+
>
|
|
384
|
+
@for (space of eligibleSpaces(); track space.id) {
|
|
385
|
+
<ion-select-option [value]="space.id">{{
|
|
386
|
+
space.title
|
|
387
|
+
}}</ion-select-option>
|
|
388
|
+
}
|
|
389
|
+
</ion-select>
|
|
390
|
+
@if (!eligibleSpaces().length) {
|
|
391
|
+
<p>No other eligible business space is available.</p>
|
|
392
|
+
}
|
|
393
|
+
<ion-button
|
|
394
|
+
[disabled]="
|
|
395
|
+
busy() || !target() || !item.canTransfer || !validRequest()
|
|
396
|
+
"
|
|
397
|
+
(click)="transfer()"
|
|
398
|
+
>Confirm transfer of paid access</ion-button
|
|
399
|
+
>
|
|
400
|
+
<ion-button
|
|
401
|
+
fill="clear"
|
|
402
|
+
[routerLink]="[basePath(), item.accountId]"
|
|
403
|
+
[queryParams]="query()"
|
|
404
|
+
[disabled]="busy()"
|
|
405
|
+
>Cancel</ion-button
|
|
406
|
+
>
|
|
407
|
+
</ion-card-content>
|
|
408
|
+
</ion-card>
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"], outputs: ["ionFocus", "ionBlur"] }, { kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonSelect, selector: "ion-select", inputs: ["cancelText", "color", "compareWith", "disabled", "errorText", "expandedIcon", "fill", "helperText", "interface", "interfaceOptions", "justify", "label", "labelPlacement", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "shape", "toggleIcon", "value"] }, { kind: "component", type: IonSelectOption, selector: "ion-select-option", inputs: ["description", "disabled", "justify", "labelPlacement", "mode", "value"] }, { kind: "pipe", type: CurrencyPipe, name: "currency" }, { kind: "pipe", type: DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
413
|
+
}
|
|
414
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SubscriptionSettingsComponent, decorators: [{
|
|
415
|
+
type: Component,
|
|
416
|
+
args: [{
|
|
417
|
+
selector: 'sneat-subscription-settings',
|
|
418
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
419
|
+
imports: [
|
|
420
|
+
RouterLink,
|
|
421
|
+
CurrencyPipe,
|
|
422
|
+
DatePipe,
|
|
423
|
+
IonButton,
|
|
424
|
+
IonCard,
|
|
425
|
+
IonCardContent,
|
|
426
|
+
IonCardHeader,
|
|
427
|
+
IonCardTitle,
|
|
428
|
+
IonItem,
|
|
429
|
+
IonLabel,
|
|
430
|
+
IonList,
|
|
431
|
+
IonSelect,
|
|
432
|
+
IonSelectOption,
|
|
433
|
+
],
|
|
434
|
+
template: `
|
|
435
|
+
@if (!identity()) {
|
|
436
|
+
<ion-card
|
|
437
|
+
><ion-card-content
|
|
438
|
+
>Sign in to manage your subscriptions.</ion-card-content
|
|
439
|
+
></ion-card
|
|
440
|
+
>
|
|
441
|
+
} @else {
|
|
442
|
+
@if (busy()) {
|
|
443
|
+
<p role="status">Checking your subscriptions…</p>
|
|
444
|
+
}
|
|
445
|
+
@if (error()) {
|
|
446
|
+
<ion-card
|
|
447
|
+
><ion-card-content
|
|
448
|
+
><p role="alert">{{ error() }}</p>
|
|
449
|
+
<ion-button fill="outline" [disabled]="busy()" (click)="refresh()"
|
|
450
|
+
>Retry</ion-button
|
|
451
|
+
>
|
|
452
|
+
</ion-card-content></ion-card
|
|
453
|
+
>
|
|
454
|
+
}
|
|
455
|
+
@if (!accountId()) {
|
|
456
|
+
@if (!busy() && !error() && !items().length) {
|
|
457
|
+
<p>No subscriptions found.</p>
|
|
458
|
+
}
|
|
459
|
+
<ion-list aria-label="Your subscriptions">
|
|
460
|
+
@for (item of items(); track item.accountId) {
|
|
461
|
+
<ion-item
|
|
462
|
+
button
|
|
463
|
+
detail
|
|
464
|
+
[routerLink]="[basePath(), item.accountId]"
|
|
465
|
+
[queryParams]="query()"
|
|
466
|
+
>
|
|
467
|
+
<ion-label class="ion-text-wrap"
|
|
468
|
+
><h2>{{ item.label }} · {{ item.spaceTitle }}</h2>
|
|
469
|
+
<p>{{ status(item) }}</p>
|
|
470
|
+
@if (item.priceConfirmed) {
|
|
471
|
+
<p>
|
|
472
|
+
{{ item.renewalMinor / 100 | currency: item.currency }} /
|
|
473
|
+
{{ item.period }}
|
|
474
|
+
</p>
|
|
475
|
+
}
|
|
476
|
+
<p>Reference {{ item.accountId.slice(-8) }}</p>
|
|
477
|
+
</ion-label>
|
|
478
|
+
</ion-item>
|
|
479
|
+
}
|
|
480
|
+
</ion-list>
|
|
481
|
+
@if (next()) {
|
|
482
|
+
<ion-button fill="clear" [disabled]="busy()" (click)="loadMore()"
|
|
483
|
+
>Show more subscriptions</ion-button
|
|
484
|
+
>
|
|
485
|
+
}
|
|
486
|
+
} @else if (selected(); as item) {
|
|
487
|
+
<ion-card>
|
|
488
|
+
<ion-card-header
|
|
489
|
+
><ion-card-title>{{ item.label }}</ion-card-title></ion-card-header
|
|
490
|
+
>
|
|
491
|
+
<ion-card-content>
|
|
492
|
+
<p>
|
|
493
|
+
Receiving space: <strong>{{ item.spaceTitle }}</strong>
|
|
494
|
+
</p>
|
|
495
|
+
<p>{{ status(item) }}</p>
|
|
496
|
+
@if (item.priceConfirmed) {
|
|
497
|
+
<p>
|
|
498
|
+
Regular price:
|
|
499
|
+
{{ item.regularMinor / 100 | currency: item.currency }} /
|
|
500
|
+
{{ item.period }}
|
|
501
|
+
</p>
|
|
502
|
+
@if (item.discountPercent) {
|
|
503
|
+
<p>
|
|
504
|
+
{{
|
|
505
|
+
item.launchDiscount
|
|
506
|
+
? 'Launch discount'
|
|
507
|
+
: 'Renewal discount'
|
|
508
|
+
}}: {{ item.discountPercent }}%
|
|
509
|
+
</p>
|
|
510
|
+
}
|
|
511
|
+
<p>
|
|
512
|
+
<strong
|
|
513
|
+
>{{
|
|
514
|
+
item.scheduledEndUTC.startsWith('0001-')
|
|
515
|
+
? 'Renewal'
|
|
516
|
+
: 'Price per period'
|
|
517
|
+
}}: {{ item.renewalMinor / 100 | currency: item.currency }} /
|
|
518
|
+
{{ item.period }}</strong
|
|
519
|
+
>
|
|
520
|
+
</p>
|
|
521
|
+
} @else {
|
|
522
|
+
<p>Price requires a payment status check.</p>
|
|
523
|
+
}
|
|
524
|
+
@if (!item.paidThroughUTC.startsWith('0001-')) {
|
|
525
|
+
<p>Paid through {{ item.paidThroughUTC | date: 'mediumDate' }}</p>
|
|
526
|
+
}
|
|
527
|
+
@if (!item.scheduledEndUTC.startsWith('0001-')) {
|
|
528
|
+
<p>
|
|
529
|
+
Renewal cancelled. Access ends
|
|
530
|
+
{{ item.scheduledEndUTC | date: 'mediumDate' }}.
|
|
531
|
+
</p>
|
|
532
|
+
}
|
|
533
|
+
@if (resume(item); as continuation) {
|
|
534
|
+
<ion-button
|
|
535
|
+
[routerLink]="continuation.path"
|
|
536
|
+
[queryParams]="continuation.query"
|
|
537
|
+
>{{
|
|
538
|
+
item.status === 'setup_required'
|
|
539
|
+
? 'Finish subscription setup'
|
|
540
|
+
: 'Check payment status'
|
|
541
|
+
}}</ion-button
|
|
542
|
+
>
|
|
543
|
+
}
|
|
544
|
+
@if (!transferView() && item.canTransfer) {
|
|
545
|
+
<ion-button
|
|
546
|
+
fill="outline"
|
|
547
|
+
[routerLink]="[basePath(), item.accountId, 'transfer']"
|
|
548
|
+
[queryParams]="transferQuery()"
|
|
549
|
+
[disabled]="busy()"
|
|
550
|
+
>Transfer paid access</ion-button
|
|
551
|
+
>
|
|
552
|
+
}
|
|
553
|
+
@if (!transferView() && item.canCancel && !confirmCancel()) {
|
|
554
|
+
<ion-button
|
|
555
|
+
fill="clear"
|
|
556
|
+
color="danger"
|
|
557
|
+
[disabled]="busy()"
|
|
558
|
+
(click)="confirmCancel.set(true)"
|
|
559
|
+
>Cancel renewal</ion-button
|
|
560
|
+
>
|
|
561
|
+
}
|
|
562
|
+
</ion-card-content>
|
|
563
|
+
</ion-card>
|
|
564
|
+
@if (confirmCancel()) {
|
|
565
|
+
<ion-card aria-label="Confirm cancellation"
|
|
566
|
+
><ion-card-content>
|
|
567
|
+
<p>
|
|
568
|
+
Cancel renewal for {{ item.label }} in {{ item.spaceTitle }}?
|
|
569
|
+
</p>
|
|
570
|
+
<p>
|
|
571
|
+
Paid access remains until
|
|
572
|
+
{{ item.paidThroughUTC | date: 'mediumDate' }}. Your other
|
|
573
|
+
subscriptions stay active.
|
|
574
|
+
</p>
|
|
575
|
+
<ion-button color="danger" [disabled]="busy()" (click)="cancel()"
|
|
576
|
+
>Confirm cancellation at period end</ion-button
|
|
577
|
+
>
|
|
578
|
+
<ion-button
|
|
579
|
+
fill="clear"
|
|
580
|
+
[disabled]="busy()"
|
|
581
|
+
(click)="confirmCancel.set(false)"
|
|
582
|
+
>Keep subscription</ion-button
|
|
583
|
+
>
|
|
584
|
+
</ion-card-content></ion-card
|
|
585
|
+
>
|
|
586
|
+
}
|
|
587
|
+
@if (transferView()) {
|
|
588
|
+
<ion-card aria-label="Transfer paid access"
|
|
589
|
+
><ion-card-header
|
|
590
|
+
><ion-card-title
|
|
591
|
+
>Transfer paid access</ion-card-title
|
|
592
|
+
></ion-card-header
|
|
593
|
+
>
|
|
594
|
+
<ion-card-content>
|
|
595
|
+
<p>
|
|
596
|
+
Projects stay in {{ item.spaceTitle }}. That space loses this
|
|
597
|
+
subscription's paid access after transfer; normal expiry
|
|
598
|
+
restrictions apply.
|
|
599
|
+
</p>
|
|
600
|
+
<p>
|
|
601
|
+
Billing dates, discount and the original introductory period
|
|
602
|
+
stay unchanged. Separate prepaid capacity and SSO add-ons stay
|
|
603
|
+
in their current space.
|
|
604
|
+
</p>
|
|
605
|
+
<p>
|
|
606
|
+
Transfers with consumed current-period allowance require
|
|
607
|
+
settlement first; transferring never grants a fresh allowance.
|
|
608
|
+
</p>
|
|
609
|
+
@if (!item.canTransfer) {
|
|
610
|
+
<p role="alert">
|
|
611
|
+
This subscription cannot currently be transferred. Check its
|
|
612
|
+
payment status first.
|
|
613
|
+
</p>
|
|
614
|
+
}
|
|
615
|
+
<ion-select
|
|
616
|
+
label="Receiving business space"
|
|
617
|
+
interface="popover"
|
|
618
|
+
[value]="target()"
|
|
619
|
+
[disabled]="busy() || !item.canTransfer"
|
|
620
|
+
(ionChange)="target.set($event.detail.value)"
|
|
621
|
+
>
|
|
622
|
+
@for (space of eligibleSpaces(); track space.id) {
|
|
623
|
+
<ion-select-option [value]="space.id">{{
|
|
624
|
+
space.title
|
|
625
|
+
}}</ion-select-option>
|
|
626
|
+
}
|
|
627
|
+
</ion-select>
|
|
628
|
+
@if (!eligibleSpaces().length) {
|
|
629
|
+
<p>No other eligible business space is available.</p>
|
|
630
|
+
}
|
|
631
|
+
<ion-button
|
|
632
|
+
[disabled]="
|
|
633
|
+
busy() || !target() || !item.canTransfer || !validRequest()
|
|
634
|
+
"
|
|
635
|
+
(click)="transfer()"
|
|
636
|
+
>Confirm transfer of paid access</ion-button
|
|
637
|
+
>
|
|
638
|
+
<ion-button
|
|
639
|
+
fill="clear"
|
|
640
|
+
[routerLink]="[basePath(), item.accountId]"
|
|
641
|
+
[queryParams]="query()"
|
|
642
|
+
[disabled]="busy()"
|
|
643
|
+
>Cancel</ion-button
|
|
644
|
+
>
|
|
645
|
+
</ion-card-content>
|
|
646
|
+
</ion-card>
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
`,
|
|
651
|
+
}]
|
|
652
|
+
}], ctorParameters: () => [], propDecorators: { accountId: [{ type: i0.Input, args: [{ isSignal: true, alias: "accountId", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], transferView: [{ type: i0.Input, args: [{ isSignal: true, alias: "transferView", required: false }] }], requestId: [{ type: i0.Input, args: [{ isSignal: true, alias: "requestId", required: false }] }], basePath: [{ type: i0.Input, args: [{ isSignal: true, alias: "basePath", required: false }] }] } });
|
|
653
|
+
//# sourceMappingURL=subscription-settings.component.js.map
|