Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add subscription data section in account settings #1838

Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 52 additions & 115 deletions packages/host/app/components/operator-mode/profile-info-popover.gts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@ import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import Component from '@glimmer/component';

import { task } from 'ember-concurrency';
import { restartableTask } from 'ember-concurrency';

import {
Avatar,
BoxelButton,
LoadingIndicator,
} from '@cardstack/boxel-ui/components';
import { cn } from '@cardstack/boxel-ui/helpers';
import { IconHexagon } from '@cardstack/boxel-ui/icons';
import perform from 'ember-concurrency/helpers/perform';

import { Avatar, BoxelButton } from '@cardstack/boxel-ui/components';
import { cn, or } from '@cardstack/boxel-ui/helpers';

import WithSubscriptionData from '@cardstack/host/components/with-subscription-data';
import config from '@cardstack/host/config/environment';
import BillingService from '@cardstack/host/services/billing-service';
import MatrixService from '@cardstack/host/services/matrix-service';
Expand Down Expand Up @@ -140,125 +138,64 @@ export default class ProfileInfoPopover extends Component<ProfileInfoPopoverSign
<ProfileInfo />
{{! TODO: Remove config.APP.stripeBillingEnabled once the API integration for credit info is completed. }}
{{#if config.APP.stripeBillingEnabled}}
<div class='credit-info' data-test-credit-info>
<div class='info-group'>
<span class='label'>Membership Tier</span>
<span class='value' data-test-membership-tier>
{{#if this.isLoading}}
<LoadingIndicator />
{{else}}
{{this.plan}}
{{/if}}
</span>
</div>
<BoxelButton
@kind='secondary-light'
@size='small'
@disabled={{this.isLoading}}
data-test-upgrade-plan-button
{{on 'click' @toggleProfileSettings}}
>Upgrade Plan</BoxelButton>
<div class='info-group'>
<span class='label'>Monthly Credit</span>
<span
class={{cn 'value' out-of-credit=this.isOutOfPlanCreditAllowance}}
data-test-monthly-credit
>
{{#if this.isLoading}}
<LoadingIndicator />
{{else}}
<IconHexagon width='16px' height='16px' />
{{this.monthlyCreditText}}
{{/if}}
</span>
</div>
<div class='info-group additional-credit'>
<span class='label'>Additional Credit</span>
<span
class={{cn 'value' out-of-credit=this.isOutOfCredit}}
data-test-additional-credit
>{{#if this.isLoading}}
<LoadingIndicator />
{{else}}
<IconHexagon width='16px' height='16px' />
{{this.extraCreditsAvailableInBalance}}
{{/if}}</span>
</div>
<div
class={{cn 'buy-more-credits' out-of-credit=this.isOutOfCredit}}
data-test-buy-more-credits
>
<WithSubscriptionData as |subscriptionData|>
<div class='credit-info' data-test-credit-info>
<div class='info-group'>
<span class='label'>Membership Tier</span>
{{subscriptionData.plan}}
</div>
<BoxelButton
@kind={{if this.isOutOfCredit 'primary' 'secondary-light'}}
@size={{if this.isOutOfCredit 'base' 'small'}}
@disabled={{this.isLoading}}
{{on 'click' @toggleProfileSettings}}
>Buy more credits</BoxelButton>
@kind='secondary-light'
@size='small'
@disabled={{or
subscriptionData.isLoading
this.managePlan.isRunning
}}
data-test-upgrade-plan-button
{{on 'click' (perform this.managePlan)}}
FadhlanR marked this conversation as resolved.
Show resolved Hide resolved
>Upgrade Plan</BoxelButton>
<div class='info-group'>
<span class='label'>Monthly Credit</span>
{{subscriptionData.monthlyCredit}}
</div>
<div class='info-group additional-credit'>
<span class='label'>Additional Credit</span>
{{subscriptionData.additionalCredit}}
</div>
<div
class={{cn
'buy-more-credits'
out-of-credit=subscriptionData.isOutOfCredit
}}
data-test-buy-more-credits
>
<BoxelButton
@kind={{if
subscriptionData.isOutOfCredit
'primary'
'secondary-light'
}}
@size={{if subscriptionData.isOutOfCredit 'base' 'small'}}
@disabled={{subscriptionData.isLoading}}
{{on 'click' @toggleProfileSettings}}
>Buy more credits</BoxelButton>
</div>
</div>
</div>
</WithSubscriptionData>
{{/if}}
</div>
</template>

constructor(...args: [any, any]) {
super(...args);
this.fetchCreditInfo.perform();
}

@service private declare billingService: BillingService;
@service declare matrixService: MatrixService;

private fetchCreditInfo = task(async () => {
await this.billingService.fetchSubscriptionData();
});
@service declare billingService: BillingService;

@action private logout() {
this.matrixService.logout();
}

private get isLoading() {
return this.billingService.fetchingSubscriptionData;
}

private get plan() {
return this.billingService.subscriptionData?.plan;
}

private get creditsIncludedInPlanAllowance() {
return this.billingService.subscriptionData?.creditsIncludedInPlanAllowance;
}

private get creditsAvailableInPlanAllowance() {
return this.billingService.subscriptionData
?.creditsAvailableInPlanAllowance;
}

private get extraCreditsAvailableInBalance() {
return this.billingService.subscriptionData?.extraCreditsAvailableInBalance;
}

private get monthlyCreditText() {
return this.creditsAvailableInPlanAllowance != null &&
this.creditsIncludedInPlanAllowance != null
? `${this.creditsAvailableInPlanAllowance} of ${this.creditsIncludedInPlanAllowance} left`
: null;
}

private get isOutOfCredit() {
return (
this.isOutOfPlanCreditAllowance &&
(this.extraCreditsAvailableInBalance == null ||
this.extraCreditsAvailableInBalance == 0)
);
}

private get isOutOfPlanCreditAllowance() {
return (
this.creditsAvailableInPlanAllowance == null ||
this.creditsIncludedInPlanAllowance == null ||
this.creditsAvailableInPlanAllowance <= 0
);
}
private managePlan = restartableTask(async () => {
await this.billingService.managePlan();
});
}

export class ProfileInfo extends Component<ProfileInfoSignature> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { isValidPassword } from '@cardstack/host/lib/matrix-utils';
import MatrixService from '@cardstack/host/services/matrix-service';

import ProfileEmail from './profile-email';
import ProfileSubscription from './profile-subscription';

interface Signature {
Args: {
Expand Down Expand Up @@ -126,6 +127,7 @@ export default class ProfileSettingsModal extends Component<Signature> {
@changeEmailComplete={{this.completeEmail}}
/>
{{/if}}
<ProfileSubscription />
</form>
{{#if (or (bool this.displayNameError) (bool this.error))}}
<div class='error-message' data-test-profile-save-error>
Expand Down
Loading