jcloud/dashboard/src/components/AppTrialSubscriptionDialog.vue

365 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<Dialog
:options="{
title: $t('Subscribe Now'),
size: '2xl'
}"
v-model="showDialog"
>
<template v-slot:body-content>
<div class="border-0">
<p class="text-base text-gray-800">
{{ $t('You are just a few steps away from creating your first website.') }}
</p>
<div class="mt-6 space-y-6">
<!-- 第一步选择计划 -->
<div>
<div v-if="step == 1">
<div class="flex items-center space-x-2">
<TextInsideCircle>1</TextInsideCircle>
<span class="text-base font-medium">{{ $t('Select Website Plan') }}</span>
</div>
<div class="pl-7">
<SitePlansCards
:hideRestrictedPlans="true"
v-model="selectedPlan"
class="mt-4"
/>
<p></p>
<div class="flex w-full justify-end">
<Button
class="mt-2 w-full sm:w-fit"
variant="solid"
@click="confirmPlan"
>
{{ $t('Confirm Plan') }}
</Button>
</div>
</div>
</div>
<div v-else>
<div class="flex items-center justify-between space-x-2">
<div class="flex items-center space-x-2">
<TextInsideCircle>1</TextInsideCircle>
<span class="text-base font-medium">
{{ $t('Website Plan Selected ({name})', { name: selectedPlan.name }) }}
</span>
</div>
<div
class="grid h-4 w-4 place-items-center rounded-full bg-green-500/90"
>
<i-lucide-check class="h-3 w-3 text-white" />
</div>
</div>
</div>
</div>
<!-- 第二步更新账单信息 -->
<div
class="rounded-md"
:class="{ 'pointer-events-none opacity-50': step < 2 }"
>
<div v-if="step <= 2">
<div class="flex items-center space-x-2">
<TextInsideCircle>2</TextInsideCircle>
<span class="text-base font-medium">
{{ $t('Update Billing Information') }}
</span>
</div>
<div class="pl-7" v-if="step == 2">
<UpdateBillingDetailsForm
@updated="onBillingAddresUpdateSuccess"
/>
</div>
</div>
<div v-else>
<div class="flex items-center justify-between space-x-2">
<div class="flex items-center space-x-2">
<TextInsideCircle>2</TextInsideCircle>
<span class="text-base font-medium">
{{ $t('Billing Information Updated') }}
</span>
</div>
<div
class="grid h-4 w-4 place-items-center rounded-full bg-green-500/90"
>
<i-lucide-check class="h-3 w-3 text-white" />
</div>
</div>
</div>
</div>
<!-- 第三步添加支付方式 -->
<div
class="rounded-md"
:class="{ 'pointer-events-none opacity-50': step < 3 }"
>
<div v-if="step <= 3">
<div class="flex items-center space-x-2">
<TextInsideCircle>3</TextInsideCircle>
<span class="text-base font-medium">{{ $t('Add Payment Method') }}</span>
</div>
<div class="mt-4 pl-7" v-if="step == 3">
<!-- 支付方式选择器 -->
<div
class="flex w-full flex-row gap-2 rounded-md border p-1 text-p-base text-gray-800"
>
<div
class="w-1/2 cursor-pointer rounded-sm py-1.5 text-center transition-all"
:class="{
'bg-gray-100': isAutomatedBilling
}"
@click="isAutomatedBilling = true"
>
{{ $t('Automated Billing') }}
</div>
<div
class="w-1/2 cursor-pointer rounded-sm py-1.5 text-center transition-all"
:class="{
'bg-gray-100': !isAutomatedBilling
}"
@click="isAutomatedBilling = false"
>
{{ $t('Recharge') }}
</div>
</div>
<div class="mt-2 w-full">
<!-- 自动计费部分 -->
<div v-if="isAutomatedBilling">
<!-- Stripe -->
<StripeCard2
@complete="onAddCardSuccess"
:withoutAddress="true"
/>
</div>
<!-- 购买预付费信用 -->
<div v-else class="mt-3">
<BuyPrepaidCreditsForm
:isOnboarding="true"
:minimumAmount="minimumAmount"
@success="onBuyCreditsSuccess"
/>
</div>
</div>
</div>
</div>
<!-- 支付方式已添加 -->
<div v-else>
<div class="flex items-center justify-between space-x-2">
<div class="flex items-center space-x-2">
<TextInsideCircle>3</TextInsideCircle>
<span
class="text-base font-medium"
v-if="$team.pg.payment_mode === 'Card'"
>
{{ $t('Automated Billing Setup Completed') }}
</span>
<span
class="text-base font-medium"
v-if="$team.pg.payment_mode === 'Prepaid Credits'"
>
{{ $t('Prepaid Credits Payment Enabled') }}
</span>
</div>
<div
class="grid h-4 w-4 place-items-center rounded-full bg-green-500/90"
>
<i-lucide-check class="h-3 w-3 text-white" />
</div>
</div>
<div
class="mt-1.5 pl-7 text-p-base text-gray-800"
v-if="$team.pg.payment_mode === 'Prepaid Credits'"
>
{{ $t('Account Balance') }}: {{ $format.userCurrency($team.pg.balance) }}
</div>
</div>
</div>
<!-- 第4步确认 -->
<div>
<div
v-if="step < 4"
class="pointer-events-none rounded-md opacity-50"
>
<div class="flex items-center justify-between space-x-2">
<div class="flex items-center space-x-2">
<TextInsideCircle>4</TextInsideCircle>
<div class="text-base font-medium">
{{ $t('Subscription Confirmation') }}
</div>
</div>
</div>
</div>
<div v-else-if="isChangingPlan">
<div class="flex items-center justify-between space-x-2">
<div class="flex items-center space-x-2">
<TextInsideCircle>4</TextInsideCircle>
<div class="text-base font-medium">{{ $t('Updating Site Plan') }}</div>
</div>
</div>
<div class="mt-3 pl-7">
<Button :loading="true" :loadingText="$t('Updating site plan...')">
{{ $t('Site Plan Updated') }}
</Button>
</div>
</div>
<div v-else>
<div class="flex items-center justify-between space-x-2">
<div class="flex items-center space-x-2">
<TextInsideCircle>4</TextInsideCircle>
<span class="text-base font-medium">
🎉 {{ $t('Subscription Confirmed') }}
</span>
</div>
<div
class="grid h-4 w-4 place-items-center rounded-full bg-green-500/90"
>
<i-lucide-check class="h-3 w-3 text-white" />
</div>
</div>
<div class="mt-1.5 pl-7">
<div class="flex items-center space-x-2">
<span class="text-p-base text-gray-800">
{{ $t('Your subscription has been confirmed.') }}<br />
{{ $t('If any of your sites have been disabled, they will be re-enabled soon.') }}
</span>
</div>
<div class="flex w-full justify-end">
<Button
class="mt-2 w-full sm:w-fit"
variant="solid"
iconRight="arrow-right"
@click="showDialog = false"
>
{{ $t('Return to Dashboard') }}
</Button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
</Dialog>
</template>
<script>
import { createResource } from 'jingrow-ui';
import { defineAsyncComponent } from 'vue';
import TextInsideCircle from './TextInsideCircle.vue';
import { toast } from 'vue-sonner';
export default {
name: 'AppTrialSubscriptionDialog',
components: {
SitePlansCards: defineAsyncComponent(() => import('./SitePlansCards.vue')),
StripeCard2: defineAsyncComponent(() =>
import('../components/StripeCard.vue')
),
UpdateBillingDetailsForm: defineAsyncComponent(() =>
import('./UpdateBillingDetailsForm.vue')
),
CardWithDetails: defineAsyncComponent(() =>
import('@/components/CardWithDetails.vue')
),
BuyPrepaidCreditsForm: defineAsyncComponent(() =>
import('./BuyPrepaidCreditsForm.vue')
),
AlertBanner: defineAsyncComponent(() => import('./AlertBanner.vue')),
TextInsideCircle
},
props: ['site', 'currentPlan', 'trialPlan'],
data() {
return {
step: 1,
selectedPlan: null,
billingInformation: {
cardHolderName: '',
country: '',
gstin: ''
},
isAutomatedBilling: true,
isChangingPlan: false
};
},
emits: ['update:modelValue', 'success'],
mounted() {
if (this.trialPlan && this.currentPlan !== this.trialPlan) {
this.selectedPlan = {
name: this.trialPlan
};
} else {
this.selectedPlan = null;
}
},
methods: {
confirmPlan() {
if (!this.selectedPlan) {
toast.error(this.$t('Please select a plan'));
return;
}
this.step = 2;
},
onBillingAddresUpdateSuccess() {
this.$team.reload();
if (this.$team.pg.payment_mode) {
this.step = 4;
this.changePlan();
} else {
this.step = 3;
}
},
onBuyCreditsSuccess() {
this.$team.reload();
this.step = 4;
this.changePlan();
},
onAddCardSuccess() {
this.$team.reload();
this.step = 4;
this.changePlan();
},
changePlan() {
if (this.isChangingPlan) return;
this.isChangingPlan = true;
const plan_name = this.selectedPlan?.name;
let request = createResource({
url: '/api/action/jcloud.api.client.run_pg_method',
params: {
dt: 'Site',
dn: this.site,
method: 'set_plan',
args: {
plan: plan_name
}
},
onSuccess: res => {
this.isChangingPlan = false;
this.$team.reload();
this.$emit('success');
},
onError: () => {
this.isChangingPlan = false;
toast.error(this.$t('Failed to change plan, please try again later.'));
this.showDialog = false;
}
});
request.submit();
}
},
computed: {
isBillingDetailsSet() {
return Boolean(this.$team.pg.billing_details?.name);
},
minimumAmount() {
return this.$team.pg.currency == 'CNY' ? 0.01 : 0.01;
},
showDialog: {
get() {
return this.modelValue;
},
set(value) {
this.$emit('update:modelValue', value);
}
}
}
};
</script>