Use router input for org slug and query params in subscription component, adjust subscription refresh handling
This commit is contained in:
1 parent
d23c695415
commit
4ed619a300
6 files changed
+297
-235
No files matched your search
+2
-1
@@ -34,7 +34,8 @@
|
||||
"prefix": "gt",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
]
|
||||
],
|
||||
"@angular-eslint/no-input-rename": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -36,6 +36,7 @@ export class OrganizationsService {
|
||||
activeOrganizationResource = resource({
|
||||
request: () => ({ organization_slug: this.activeOrganizationSlug() }),
|
||||
loader: async ({ request }) => {
|
||||
console.log("loading for some reasn")
|
||||
if (!request.organization_slug) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { computed, Injectable, inject, resource } from "@angular/core";
|
||||
import { computed, Injectable, inject, resource, signal } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { lastValueFrom } from "rxjs";
|
||||
import { tap } from "rxjs/operators";
|
||||
@@ -6,12 +6,12 @@ import { EventsCount } from "./subscriptions.interfaces";
|
||||
import { StatefulService } from "src/app/shared/stateful-service/signal-state.service";
|
||||
import { SubscriptionsAPIService } from "./subscriptions-api.service";
|
||||
import { client } from "../api";
|
||||
import { OrganizationsService } from "../organizations.service";
|
||||
import { SettingsService } from "../settings.service";
|
||||
|
||||
interface SubscriptionsState {
|
||||
billingPortalLoading: boolean;
|
||||
billingPortalLoadingError: string;
|
||||
subscriptionRefreshing: boolean;
|
||||
subscriptionLoadingTimeout: boolean;
|
||||
fromStripe: boolean;
|
||||
eventsCount: EventsCount | null;
|
||||
@@ -20,6 +20,7 @@ interface SubscriptionsState {
|
||||
const initialState: SubscriptionsState = {
|
||||
billingPortalLoading: false,
|
||||
billingPortalLoadingError: "",
|
||||
subscriptionRefreshing: false,
|
||||
subscriptionLoadingTimeout: false,
|
||||
fromStripe: false,
|
||||
eventsCount: null,
|
||||
@@ -29,20 +30,17 @@ const initialState: SubscriptionsState = {
|
||||
providedIn: "root",
|
||||
})
|
||||
export class SubscriptionsService extends StatefulService<SubscriptionsState> {
|
||||
private organizationsService = inject(OrganizationsService);
|
||||
private settingsService = inject(SettingsService);
|
||||
private subscriptionsAPIService = inject(SubscriptionsAPIService);
|
||||
private router = inject(Router);
|
||||
|
||||
stripePublicKey = this.settingsService.stripePublicKey;
|
||||
|
||||
subscriptionLoading = computed(() => this.subscriptionResource.isLoading());
|
||||
subscriptionLoadingTimeout = computed(
|
||||
() => this.state().subscriptionLoadingTimeout
|
||||
);
|
||||
organizationSlug = signal<string>("");
|
||||
|
||||
subscriptionResource = resource({
|
||||
request: () => ({
|
||||
orgSlug: this.organizationsService.activeOrganizationSlug(),
|
||||
orgSlug: this.organizationSlug(),
|
||||
}),
|
||||
loader: async ({ request }) => {
|
||||
if (!request.orgSlug) {
|
||||
@@ -74,10 +72,18 @@ export class SubscriptionsService extends StatefulService<SubscriptionsState> {
|
||||
};
|
||||
return formattedSubscription;
|
||||
});
|
||||
subscriptionLoading = computed(
|
||||
() =>
|
||||
this.subscriptionResource.isLoading() ||
|
||||
this.state().subscriptionRefreshing
|
||||
);
|
||||
subscriptionLoadingTimeout = computed(
|
||||
() => this.state().subscriptionLoadingTimeout
|
||||
);
|
||||
|
||||
eventCountResource = resource({
|
||||
request: () => ({
|
||||
orgSlug: this.organizationsService.activeOrganizationSlug(),
|
||||
orgSlug: this.organizationSlug(),
|
||||
}),
|
||||
loader: async ({ request }) => {
|
||||
if (!request.orgSlug) {
|
||||
@@ -98,7 +104,6 @@ export class SubscriptionsService extends StatefulService<SubscriptionsState> {
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
||||
eventsCountWithTotal = computed(() => {
|
||||
const eventsCount = this.eventCountResource.value();
|
||||
if (!eventsCount) return eventsCount;
|
||||
@@ -116,7 +121,6 @@ export class SubscriptionsService extends StatefulService<SubscriptionsState> {
|
||||
billingPortalLoadingError = computed(
|
||||
() => this.state().billingPortalLoadingError
|
||||
);
|
||||
|
||||
fromStripe = computed(() => this.state().fromStripe);
|
||||
totalEventsAllowed = computed(() => {
|
||||
const subscription = this.subscription();
|
||||
@@ -127,33 +131,11 @@ export class SubscriptionsService extends StatefulService<SubscriptionsState> {
|
||||
super(initialState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep trying to get subscription, for users redirected from Stripe
|
||||
* @param slug Organization Slug for requested subscription
|
||||
*/
|
||||
// retrieveUntilSubscriptionOrTimeout(slug: string) {
|
||||
// this.setSubscriptionLoadingStart(true);
|
||||
// lastValueFrom(
|
||||
// this.subscriptionsAPIService.retrieve(slug).pipe(
|
||||
// expand((subscription) => {
|
||||
// if (!subscription.created) {
|
||||
// return this.subscriptionsAPIService
|
||||
// .retrieve(slug)
|
||||
// .pipe(delay(2000));
|
||||
// } else {
|
||||
// this.setSubscription(subscription);
|
||||
// return EMPTY;
|
||||
// }
|
||||
// }),
|
||||
// catchError(() => {
|
||||
// this.setSubscriptionLoadingError();
|
||||
// return EMPTY;
|
||||
// }),
|
||||
// takeUntil(this.subscriptionRetryTimer())
|
||||
// ),
|
||||
// { defaultValue: null }
|
||||
// );
|
||||
// }
|
||||
retrieveSubscriptionData(orgSlug: string) {
|
||||
if (orgSlug) {
|
||||
this.organizationSlug.set(orgSlug);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve Subscription and navigate to subscription page if no subscription exists
|
||||
@@ -182,7 +164,7 @@ export class SubscriptionsService extends StatefulService<SubscriptionsState> {
|
||||
|
||||
async redirectToBillingPortal() {
|
||||
this.setBillingPortalLoadingStart();
|
||||
const orgSlug = this.organizationsService.activeOrganizationSlug();
|
||||
const orgSlug = this.organizationSlug();
|
||||
const { data, error } = await client.POST(
|
||||
"/api/0/stripe/organizations/{organization_slug}/create-billing-portal/",
|
||||
{
|
||||
@@ -201,36 +183,55 @@ export class SubscriptionsService extends StatefulService<SubscriptionsState> {
|
||||
}
|
||||
}
|
||||
|
||||
// private subscriptionRetryTimer() {
|
||||
// return timer(60000).pipe(
|
||||
// tap(() => {
|
||||
// this.setSubscriptionLoadingTimeout();
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
/**
|
||||
* Keep trying to get subscription, for users redirected from Stripe
|
||||
*/
|
||||
refreshUntilSubscriptionOrTimeout() {
|
||||
this.setSubscriptionRefreshingStart(true);
|
||||
let i = 0;
|
||||
const intervalRef = setInterval(() => {
|
||||
this.subscriptionResource.reload();
|
||||
if (this.subscription()) {
|
||||
this.setSubscriptionRefreshingComplete();
|
||||
clearInterval(intervalRef);
|
||||
} else if (i === 2) {
|
||||
this.setSubscriptionRefreshingTimeout();
|
||||
clearInterval(intervalRef);
|
||||
}
|
||||
i++;
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// private setSubscriptionLoadingStart(fromStripe: boolean = false) {
|
||||
// this.setState({ subscriptionLoading: true, fromStripe });
|
||||
// }
|
||||
private setSubscriptionRefreshingStart(fromStripe: boolean = false) {
|
||||
this.setState({ subscriptionRefreshing: true, fromStripe });
|
||||
}
|
||||
|
||||
// private setSubscriptionLoadingError() {
|
||||
// this.setState({ subscriptionLoading: false });
|
||||
// }
|
||||
private setSubscriptionRefreshingComplete(fromStripe: boolean = false) {
|
||||
this.setState({ subscriptionRefreshing: false });
|
||||
}
|
||||
|
||||
// private setSubscriptionLoadingTimeout() {
|
||||
// this.setState({
|
||||
// subscriptionLoadingTimeout: true,
|
||||
// });
|
||||
// }
|
||||
private setSubscriptionRefreshingTimeout() {
|
||||
this.setState({
|
||||
subscriptionRefreshing: false,
|
||||
subscriptionLoadingTimeout: true,
|
||||
});
|
||||
}
|
||||
|
||||
setBillingPortalLoadingStart() {
|
||||
private setBillingPortalLoadingStart() {
|
||||
this.setState({ billingPortalLoading: true });
|
||||
}
|
||||
|
||||
setBillingPortalLoadingError(message: string) {
|
||||
private setBillingPortalLoadingError(message: string) {
|
||||
this.setState({
|
||||
billingPortalLoading: false,
|
||||
billingPortalLoadingError: message,
|
||||
});
|
||||
}
|
||||
|
||||
clearState() {
|
||||
super.clearState();
|
||||
this.organizationSlug.set("");
|
||||
this.subscriptionResource.set(undefined);
|
||||
this.eventCountResource.set(undefined);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, inject } from "@angular/core";
|
||||
import { Component, ChangeDetectionStrategy, inject } from "@angular/core";
|
||||
import { environment } from "../../../../environments/environment";
|
||||
import { EventInfoComponent } from "../../../shared/event-info/event-info.component";
|
||||
import { MatDividerModule } from "@angular/material/divider";
|
||||
@@ -23,7 +23,7 @@ import { OrganizationsService } from "src/app/api/organizations.service";
|
||||
DecimalPipe,
|
||||
],
|
||||
})
|
||||
export class PaymentComponent implements OnInit {
|
||||
export class PaymentComponent {
|
||||
private organizationService = inject(OrganizationsService);
|
||||
private paymentService = inject(PaymentService);
|
||||
|
||||
@@ -32,10 +32,6 @@ export class PaymentComponent implements OnInit {
|
||||
this.paymentService.subscriptionCreationLoadingId;
|
||||
billingEmail = environment.billingEmail;
|
||||
|
||||
ngOnInit() {
|
||||
this.organizationService.activeOrganizationResource.reload();
|
||||
}
|
||||
|
||||
onSubmit(price: Price) {
|
||||
const activeOrganization = this.organizationService.activeOrganization();
|
||||
if (activeOrganization) {
|
||||
|
||||
@@ -2,142 +2,212 @@
|
||||
<header class="page-header">
|
||||
<h2 class="mat-headline-4" i18n>Subscription</h2>
|
||||
@if (fromStripe()) {
|
||||
<span class="mat-subtitle-1">Redirected from Stripe</span>
|
||||
<span class="mat-subtitle-1">Redirected from Stripe</span>
|
||||
}
|
||||
</header>
|
||||
@if (!(subscriptionLoading())) { @if (subscription(); as subscription) {
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<div>
|
||||
@if ( subscription.status === "active" || subscription.status ===
|
||||
"trialing" || subscription.status === "past_due" ) { @if
|
||||
(subscription.status === "active") {
|
||||
<p class="mat-body-1">
|
||||
@if (subscription.effectivePrice === 0) {
|
||||
<ng-container i18n>You have selected the free plan. Please consider making a monthly
|
||||
<a href="https://liberapay.com/GlitchTip/donate" target="_blank">donation</a>
|
||||
to help keep open source development sustainable. Thank you for
|
||||
choosing GlitchTip!</ng-container>
|
||||
} @else {
|
||||
<ng-container i18n>This organization has an active subscription to GlitchTip. Thank
|
||||
you!</ng-container>
|
||||
}
|
||||
</p>
|
||||
} @if (subscription.status === "trialing") {
|
||||
<p class="mat-body-1 no-bottom-margin" i18n>This organization has a trial subscription to GlitchTip.</p>
|
||||
} @if (subscription.status === "past_due") {
|
||||
<p class="mat-body-1" i18n>This organization has a subscription to GlitchTip, but the payment is
|
||||
past due.</p>
|
||||
@if (subscriptionLoading() || subscriptionLoadingTimeout()) {
|
||||
<mat-card>
|
||||
<mat-card-header></mat-card-header>
|
||||
<mat-card-content>
|
||||
@if (subscriptionLoading()) {
|
||||
<div class="spinner-container">
|
||||
<mat-spinner diameter="36"></mat-spinner>
|
||||
</div>
|
||||
}
|
||||
<ng-container>
|
||||
<mat-card-subtitle i18n>Details</mat-card-subtitle>
|
||||
<h3>{{ subscription.product.name }}</h3>
|
||||
{{ subscription.product.description }}
|
||||
@if (eventsPercent(); as eventsPercent) {
|
||||
<div class="visualization">
|
||||
<div class="total-events">
|
||||
{{ totalEventsAllowed() }}
|
||||
</div>
|
||||
<div class="display-bar">
|
||||
<div class="file-size-percent" [style.width.%]="eventsPercent.fileSize"></div>
|
||||
<div class="error-event" [style.width.%]="eventsPercent.errorEvents"></div>
|
||||
<div class="uptime-event" [style.width.%]="eventsPercent.uptimeEvents"></div>
|
||||
<div class="transaction-event" [style.width.%]="eventsPercent.transactionEvents"></div>
|
||||
</div>
|
||||
@if (eventsCountWithTotal(); as count) {
|
||||
<div class="event-count" [class.event-count--left]="eventsPercent.total < 25"
|
||||
[class.event-count--right]="eventsPercent.total > 75" style="left: {{
|
||||
eventsPercent.total < 100 ? eventsPercent.total : 100
|
||||
}}%">
|
||||
{{ count.total }}
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li>
|
||||
<b class="error" i18n>Issue events:</b>
|
||||
{{ count.eventCount }}
|
||||
</li>
|
||||
<li>
|
||||
<b class="uptime" i18n>Uptime events:</b>
|
||||
{{ count.uptimeCheckEventCount }}
|
||||
</li>
|
||||
<li>
|
||||
<b class="performance" i18n>Performance events:</b>
|
||||
{{ count.transactionEventCount }}
|
||||
</li>
|
||||
<li>
|
||||
<b class="file-size" i18n>File storage:</b>
|
||||
{{ count.fileSizeMb }} MB
|
||||
</li>
|
||||
<li i18n>Total
|
||||
<a [routerLink]="[]" (click)="openEventInfoDialog()">events</a>
|
||||
used in billing period: {{ count.total }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@if (subscriptionLoadingTimeout()) {
|
||||
<div>
|
||||
GlitchTip was unable to find a subscription for your account. If you
|
||||
just signed up for a subscription on our payment platform, please
|
||||
contact us at
|
||||
<a href="mailto:{{ billingEmail }}">{{ billingEmail }}</a
|
||||
>.
|
||||
</div>
|
||||
}
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
} @else {
|
||||
@if (subscription(); as subscription) {
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<div>
|
||||
@if (
|
||||
subscription.status === "active" ||
|
||||
subscription.status === "trialing" ||
|
||||
subscription.status === "past_due"
|
||||
) {
|
||||
@if (subscription.status === "active") {
|
||||
<p class="mat-body-1">
|
||||
@if (subscription.effectivePrice === 0) {
|
||||
<ng-container i18n
|
||||
>You have selected the free plan. Please consider making a
|
||||
monthly
|
||||
<a
|
||||
href="https://liberapay.com/GlitchTip/donate"
|
||||
target="_blank"
|
||||
>donation</a
|
||||
>
|
||||
to help keep open source development sustainable. Thank
|
||||
you for choosing GlitchTip!</ng-container
|
||||
>
|
||||
} @else {
|
||||
<ng-container i18n
|
||||
>This organization has an active subscription to
|
||||
GlitchTip. Thank you!</ng-container
|
||||
>
|
||||
}
|
||||
</p>
|
||||
}
|
||||
@if (subscription.status === "trialing") {
|
||||
<p class="mat-body-1 no-bottom-margin" i18n
|
||||
>This organization has a trial subscription to GlitchTip.</p
|
||||
>
|
||||
}
|
||||
@if (subscription.status === "past_due") {
|
||||
<p class="mat-body-1" i18n
|
||||
>This organization has a subscription to GlitchTip, but the
|
||||
payment is past due.</p
|
||||
>
|
||||
}
|
||||
<ng-container>
|
||||
<mat-card-subtitle i18n>Details</mat-card-subtitle>
|
||||
<h3>{{ subscription.product.name }}</h3>
|
||||
{{ subscription.product.description }}
|
||||
@if (eventsPercent(); as eventsPercent) {
|
||||
<div class="visualization">
|
||||
<div class="total-events">
|
||||
{{ totalEventsAllowed() }}
|
||||
</div>
|
||||
<div class="display-bar">
|
||||
<div
|
||||
class="file-size-percent"
|
||||
[style.width.%]="eventsPercent.fileSize"
|
||||
></div>
|
||||
<div
|
||||
class="error-event"
|
||||
[style.width.%]="eventsPercent.errorEvents"
|
||||
></div>
|
||||
<div
|
||||
class="uptime-event"
|
||||
[style.width.%]="eventsPercent.uptimeEvents"
|
||||
></div>
|
||||
<div
|
||||
class="transaction-event"
|
||||
[style.width.%]="eventsPercent.transactionEvents"
|
||||
></div>
|
||||
</div>
|
||||
@if (eventsCountWithTotal(); as count) {
|
||||
<div
|
||||
class="event-count"
|
||||
[class.event-count--left]="eventsPercent.total < 25"
|
||||
[class.event-count--right]="eventsPercent.total > 75"
|
||||
style="left: {{
|
||||
eventsPercent.total < 100 ? eventsPercent.total : 100
|
||||
}}%"
|
||||
>
|
||||
{{ count.total }}
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li>
|
||||
<b class="error" i18n>Issue events:</b>
|
||||
{{ count.eventCount }}
|
||||
</li>
|
||||
<li>
|
||||
<b class="uptime" i18n>Uptime events:</b>
|
||||
{{ count.uptimeCheckEventCount }}
|
||||
</li>
|
||||
<li>
|
||||
<b class="performance" i18n>Performance events:</b>
|
||||
{{ count.transactionEventCount }}
|
||||
</li>
|
||||
<li>
|
||||
<b class="file-size" i18n>File storage:</b>
|
||||
{{ count.fileSizeMb }} MB
|
||||
</li>
|
||||
<li i18n
|
||||
>Total
|
||||
<a [routerLink]="[]" (click)="openEventInfoDialog()"
|
||||
>events</a
|
||||
>
|
||||
used in billing period: {{ count.total }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<ul>
|
||||
<li class="mat-body-1" i18n
|
||||
>Start date for this plan:
|
||||
{{ subscription.startDate | date }}</li
|
||||
>
|
||||
<li class="mat-body-1" i18n
|
||||
>Plan cost: {{ subscription.effectivePrice | currency }}</li
|
||||
>
|
||||
<li class="mat-body-1">
|
||||
<ng-container i18n>Billing</ng-container>:
|
||||
@if (
|
||||
subscription.collectionMethod === "charge_automatically"
|
||||
) {
|
||||
<ng-container i18n>Automatic</ng-container>
|
||||
}
|
||||
@if (subscription.collectionMethod === "send_invoice") {
|
||||
<ng-container i18n>Send invoice</ng-container>
|
||||
}
|
||||
</li>
|
||||
<li class="mat-body-1" i18n
|
||||
>Billing period:
|
||||
{{ subscription.currentPeriodStart | date }} to
|
||||
{{ subscription.currentPeriodEnd | date }}</li
|
||||
>
|
||||
</ul>
|
||||
</ng-container>
|
||||
<p class="mat-body-1" i18n
|
||||
>If you have questions about your subscription, please contact
|
||||
us at <a href="mailto:{{ billingEmail }}">{{ billingEmail }}</a
|
||||
>.
|
||||
</p>
|
||||
<mat-error>{{ billingPortalLoadingError() }}</mat-error>
|
||||
<gt-loading-button
|
||||
color="primary"
|
||||
(click)="manageSubscription()"
|
||||
[loading]="billingPortalLoading()"
|
||||
buttonText="Manage Subscription"
|
||||
/>
|
||||
} @else {
|
||||
<p class="mat-body-1" i18n
|
||||
>This organization does not have an active subscription to
|
||||
GlitchTip. Pick a plan to get started.</p
|
||||
>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<ul>
|
||||
<li class="mat-body-1" i18n>Start date for this plan:
|
||||
{{ subscription.startDate | date }}</li>
|
||||
<li class="mat-body-1" i18n>Plan cost: {{ subscription.effectivePrice | currency }}</li>
|
||||
<li class="mat-body-1">
|
||||
<ng-container i18n>Billing</ng-container>: @if (
|
||||
subscription.collectionMethod === "charge_automatically" ) {
|
||||
<ng-container i18n>Automatic</ng-container>
|
||||
} @if (subscription.collectionMethod === "send_invoice") {
|
||||
<ng-container i18n>Send invoice</ng-container>
|
||||
}
|
||||
</li>
|
||||
<li class="mat-body-1" i18n>Billing period: {{ subscription.currentPeriodStart | date }} to
|
||||
{{ subscription.currentPeriodEnd | date }}</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
<p class="mat-body-1" i18n>If you have questions about your subscription, please contact us at
|
||||
<a href="mailto:{{ billingEmail }}">{{ billingEmail }}</a>.
|
||||
</p>
|
||||
<mat-error>{{ billingPortalLoadingError() }}</mat-error>
|
||||
<gt-loading-button color="primary" (click)="manageSubscription()" [loading]="billingPortalLoading()" buttonText="Manage Subscription" />
|
||||
} @else {
|
||||
<p class="mat-body-1" i18n>This organization does not have an active subscription to GlitchTip.
|
||||
Pick a plan to get started.</p>
|
||||
}
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
@if (promptForProject()) {
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<p i18n>Now that you have an organization and a subscription, create a
|
||||
project.</p>
|
||||
@if (activeOrganizationSlug(); as slug) {
|
||||
<a mat-flat-button [routerLink]="['/', slug, 'settings', 'projects', 'new']" color="primary" i18n>Create New
|
||||
Project</a>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
@if (promptForProject()) {
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<p i18n
|
||||
>Now that you have an organization and a subscription, create a
|
||||
project.</p
|
||||
>
|
||||
@if (activeOrganizationSlug(); as slug) {
|
||||
<a
|
||||
mat-flat-button
|
||||
[routerLink]="['/', slug, 'settings', 'projects', 'new']"
|
||||
color="primary"
|
||||
i18n
|
||||
>Create New Project</a
|
||||
>
|
||||
}
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
}
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
} } @if ( (subscriptionLoading() === false && (subscription() ) === null) ||
|
||||
(subscription())!.status === "canceled" ) {
|
||||
<gt-payment></gt-payment>
|
||||
} } @else { @if ( (subscriptionLoading()) || (subscriptionLoadingTimeout()) )
|
||||
{
|
||||
<mat-card>
|
||||
<mat-card-header></mat-card-header>
|
||||
<mat-card-content>
|
||||
@if (subscriptionLoading()) {
|
||||
<div class="spinner-container">
|
||||
<mat-spinner diameter="36"></mat-spinner>
|
||||
</div>
|
||||
} @if (subscriptionLoadingTimeout()) {
|
||||
<div>
|
||||
GlitchTip was unable to find a subscription for your account. If you
|
||||
just signed up for a subscription on our payment platform, please
|
||||
contact us at
|
||||
<a href="mailto:{{ billingEmail }}">{{ billingEmail }}</a>.
|
||||
</div>
|
||||
}
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
} }
|
||||
}
|
||||
@if (
|
||||
(subscriptionLoading() === false && subscription() === null) ||
|
||||
subscription()!.status === "canceled"
|
||||
) {
|
||||
<gt-payment></gt-payment>
|
||||
}
|
||||
}
|
||||
</section>
|
||||
@@ -1,8 +1,14 @@
|
||||
import { Component, ChangeDetectionStrategy, OnDestroy, computed, inject } from "@angular/core";
|
||||
import { ActivatedRoute, RouterLink } from "@angular/router";
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
OnDestroy,
|
||||
computed,
|
||||
inject,
|
||||
input,
|
||||
OnInit,
|
||||
} from "@angular/core";
|
||||
import { RouterLink } from "@angular/router";
|
||||
import { MatDialog, MatDialogModule } from "@angular/material/dialog";
|
||||
import { combineLatest, Subscription } from "rxjs";
|
||||
import { map, filter, take } from "rxjs/operators";
|
||||
import { EventInfoComponent } from "src/app/shared/event-info/event-info.component";
|
||||
import { environment } from "../../../environments/environment";
|
||||
import { SubscriptionsService } from "src/app/api/subscriptions/subscriptions.service";
|
||||
@@ -41,11 +47,16 @@ interface Percentages {
|
||||
DatePipe,
|
||||
],
|
||||
})
|
||||
export class SubscriptionComponent implements OnDestroy {
|
||||
export class SubscriptionComponent implements OnDestroy, OnInit {
|
||||
private service = inject(SubscriptionsService);
|
||||
private route = inject(ActivatedRoute);
|
||||
dialog = inject(MatDialog);
|
||||
private orgService = inject(OrganizationsService);
|
||||
dialog = inject(MatDialog);
|
||||
|
||||
orgSlug = input.required<string>({ alias: "org-slug" });
|
||||
sessionId = input<string>("", { alias: "session_id" });
|
||||
billingPortalRedirect = input<string>("", {
|
||||
alias: "billing_portal_redirect",
|
||||
});
|
||||
|
||||
fromStripe = this.service.fromStripe;
|
||||
subscription = this.service.subscription;
|
||||
@@ -55,8 +66,8 @@ export class SubscriptionComponent implements OnDestroy {
|
||||
totalEventsAllowed = this.service.totalEventsAllowed;
|
||||
activeOrganization = this.orgService.activeOrganization;
|
||||
activeOrganizationSlug = this.orgService.activeOrganizationSlug;
|
||||
billingPortalLoading = this.service.billingPortalLoading
|
||||
billingPortalLoadingError = this.service.billingPortalLoadingError
|
||||
billingPortalLoading = this.service.billingPortalLoading;
|
||||
billingPortalLoadingError = this.service.billingPortalLoadingError;
|
||||
|
||||
promptForProject = computed(() => {
|
||||
const status = this.orgService.activeOrganizationLoaded();
|
||||
@@ -73,7 +84,6 @@ export class SubscriptionComponent implements OnDestroy {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
routerSubscription: Subscription;
|
||||
billingEmail = environment.billingEmail;
|
||||
eventsPercent = computed<Percentages>(() => {
|
||||
const eventsAllowed = this.totalEventsAllowed();
|
||||
@@ -88,32 +98,16 @@ export class SubscriptionComponent implements OnDestroy {
|
||||
};
|
||||
});
|
||||
|
||||
constructor() {
|
||||
this.routerSubscription = combineLatest([
|
||||
this.route.params,
|
||||
this.route.queryParams,
|
||||
])
|
||||
.pipe(
|
||||
map(([params, queryParams]) => {
|
||||
const slug = params["org-slug"] as string;
|
||||
const sessionId = queryParams["session_id"];
|
||||
const redirectFromBillingPortal =
|
||||
queryParams["billing_portal_redirect"];
|
||||
return { slug, sessionId, redirectFromBillingPortal };
|
||||
}),
|
||||
filter((routerData) => !!routerData.slug),
|
||||
take(1)
|
||||
)
|
||||
.subscribe((routerData) => {
|
||||
if (routerData.sessionId) {
|
||||
// this.service.retrieveUntilSubscriptionOrTimeout(routerData.slug);
|
||||
} else {
|
||||
this.service.subscriptionResource.reload();
|
||||
}
|
||||
if (routerData.redirectFromBillingPortal) {
|
||||
this.orgService.repeatRefreshOrgDetail();
|
||||
}
|
||||
});
|
||||
ngOnInit(): void {
|
||||
this.orgService.activeOrganizationResource.reload();
|
||||
this.service.retrieveSubscriptionData(this.orgSlug());
|
||||
|
||||
if (this.sessionId()) {
|
||||
this.service.refreshUntilSubscriptionOrTimeout();
|
||||
}
|
||||
if (this.billingPortalRedirect()) {
|
||||
this.orgService.repeatRefreshOrgDetail();
|
||||
}
|
||||
}
|
||||
|
||||
openEventInfoDialog() {
|
||||
@@ -127,7 +121,6 @@ export class SubscriptionComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.routerSubscription.unsubscribe();
|
||||
this.service.clearState();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user