Fix: Various pricing plan fixes (#4244)
This commit is contained in:
@@ -5,8 +5,7 @@
|
||||
<div class="lg:h-32">
|
||||
<div class="flex items-center gap-x-2">
|
||||
<h4 class="text-body font-medium">
|
||||
Workspace
|
||||
<span class="capitalize">{{ plan }}</span>
|
||||
{{ formatName(plan) }}
|
||||
</h4>
|
||||
<CommonBadge v-if="badgeText" rounded>
|
||||
{{ badgeText }}
|
||||
@@ -92,10 +91,9 @@ import {
|
||||
WorkspacePlanStatuses,
|
||||
BillingInterval
|
||||
} from '~/lib/common/generated/gql/graphql'
|
||||
import { startCase } from 'lodash'
|
||||
import { XMarkIcon } from '@heroicons/vue/24/outline'
|
||||
import { useWorkspacePlanPrices } from '~/lib/billing/composables/prices'
|
||||
import { formatPrice } from '~/lib/billing/helpers/prices'
|
||||
import { formatPrice, formatName } from '~/lib/billing/helpers/plan'
|
||||
import { useBillingActions } from '~/lib/billing/composables/actions'
|
||||
|
||||
defineEmits<{
|
||||
@@ -108,11 +106,12 @@ const props = defineProps<{
|
||||
currentPlan: MaybeNullOrUndefined<WorkspacePlan>
|
||||
isAdmin: boolean
|
||||
activeBillingInterval: MaybeNullOrUndefined<BillingInterval>
|
||||
hasSubscription: boolean
|
||||
workspaceId: MaybeNullOrUndefined<string>
|
||||
}>()
|
||||
|
||||
const { pricesNew } = useWorkspacePlanPrices()
|
||||
const { upgradePlan } = useBillingActions()
|
||||
const { upgradePlan, redirectToCheckout } = useBillingActions()
|
||||
|
||||
const isYearlyIntervalSelected = ref(props.yearlyIntervalSelected)
|
||||
|
||||
@@ -204,19 +203,20 @@ const isSelectable = computed(() => {
|
||||
})
|
||||
|
||||
const buttonColor = computed(() => {
|
||||
if (props.currentPlan?.status === WorkspacePlanStatuses.Expired) {
|
||||
return props.plan === WorkspacePlans.Starter ? 'primary' : 'outline'
|
||||
if (props.currentPlan?.name === WorkspacePlans.Free) {
|
||||
return props.plan === WorkspacePlans.Pro ? 'primary' : 'outline'
|
||||
}
|
||||
return 'outline'
|
||||
})
|
||||
|
||||
const buttonText = computed(() => {
|
||||
// Allow selection during trial, expired or canceled state
|
||||
// Allow if current plan is Free, or the current plan is expired/canceled
|
||||
if (
|
||||
props.currentPlan?.name === WorkspacePlans.Free ||
|
||||
props.currentPlan?.status === WorkspacePlanStatuses.Expired ||
|
||||
props.currentPlan?.status === WorkspacePlanStatuses.Canceled
|
||||
) {
|
||||
return `Subscribe to ${startCase(props.plan)}`
|
||||
return `Subscribe to ${formatName(props.plan)}`
|
||||
}
|
||||
// Current plan case
|
||||
if (isCurrentPlan.value) {
|
||||
@@ -234,7 +234,7 @@ const buttonText = computed(() => {
|
||||
return 'Change to annual plan'
|
||||
}
|
||||
// Upgrade case
|
||||
return canUpgradeToPlan.value ? `Upgrade to ${startCase(props.plan)}` : ''
|
||||
return canUpgradeToPlan.value ? `Upgrade to ${formatName(props.plan)}` : ''
|
||||
})
|
||||
|
||||
const badgeText = computed(() =>
|
||||
@@ -243,7 +243,9 @@ const badgeText = computed(() =>
|
||||
|
||||
const handleUpgradeClick = () => {
|
||||
if (!props.workspaceId) return
|
||||
if (props.plan === WorkspacePlans.Team || props.plan === WorkspacePlans.Pro) {
|
||||
if (props.plan !== WorkspacePlans.Team && props.plan !== WorkspacePlans.Pro) return
|
||||
|
||||
if (props.hasSubscription) {
|
||||
upgradePlan({
|
||||
plan: props.plan,
|
||||
cycle: isYearlyIntervalSelected.value
|
||||
@@ -251,6 +253,14 @@ const handleUpgradeClick = () => {
|
||||
: BillingInterval.Monthly,
|
||||
workspaceId: props.workspaceId
|
||||
})
|
||||
} else {
|
||||
redirectToCheckout({
|
||||
plan: props.plan,
|
||||
cycle: isYearlyIntervalSelected.value
|
||||
? BillingInterval.Yearly
|
||||
: BillingInterval.Monthly,
|
||||
workspaceId: props.workspaceId
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
:active-billing-interval="billingInterval"
|
||||
:is-admin="isAdmin"
|
||||
:workspace-id="props.workspaceId"
|
||||
:has-subscription="!!subscription"
|
||||
@on-yearly-interval-selected="onYearlyIntervalSelected"
|
||||
/>
|
||||
</div>
|
||||
@@ -34,7 +35,11 @@ const props = defineProps<{
|
||||
workspaceId: MaybeNullOrUndefined<string>
|
||||
}>()
|
||||
|
||||
const { billingInterval, plan: currentPlan } = useWorkspacePlan(props.slug)
|
||||
const {
|
||||
billingInterval,
|
||||
plan: currentPlan,
|
||||
subscription
|
||||
} = useWorkspacePlan(props.slug)
|
||||
|
||||
const isYearlySelected = ref(false)
|
||||
|
||||
|
||||
@@ -229,7 +229,7 @@ import { useMixpanel } from '~/lib/core/composables/mp'
|
||||
import { guideBillingUrl } from '~/lib/common/helpers/route'
|
||||
import { adminUpdateWorkspacePlanMutation } from '~/lib/billing/graphql/mutations'
|
||||
import { useWorkspacePlanPrices } from '~/lib/billing/composables/prices'
|
||||
import { formatPrice } from '~/lib/billing/helpers/prices'
|
||||
import { formatPrice } from '~/lib/billing/helpers/plan'
|
||||
|
||||
graphql(`
|
||||
fragment SettingsWorkspacesBilling_Workspace on Workspace {
|
||||
|
||||
@@ -120,7 +120,7 @@ import { startCase, isFunction } from 'lodash'
|
||||
import { XMarkIcon } from '@heroicons/vue/24/outline'
|
||||
import type { SetupContext } from 'vue'
|
||||
import { useWorkspacePlanPrices } from '~/lib/billing/composables/prices'
|
||||
import { formatPrice } from '~/lib/billing/helpers/prices'
|
||||
import { formatPrice } from '~/lib/billing/helpers/plan'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'onYearlyIntervalSelected', value: boolean): void
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="p-5 pt-4 flex flex-col">
|
||||
<h3 class="text-body-xs text-foreground-2 pb-4">Current plan</h3>
|
||||
<p class="text-heading-lg text-foreground capitalize">
|
||||
{{ plan?.name }}
|
||||
{{ formatName(plan?.name) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
import { useWorkspacePlan } from '~~/lib/workspaces/composables/plan'
|
||||
import { useBillingActions } from '~/lib/billing/composables/actions'
|
||||
import type { MaybeNullOrUndefined } from '@speckle/shared'
|
||||
import { formatName } from '~/lib/billing/helpers/plan'
|
||||
|
||||
defineProps<{
|
||||
workspaceId?: MaybeNullOrUndefined<string>
|
||||
|
||||
@@ -31,7 +31,7 @@ import type { PaidWorkspacePlansOld } from '@speckle/shared'
|
||||
import { Roles } from '@speckle/shared'
|
||||
import { isPaidPlan } from '~/lib/billing/helpers/types'
|
||||
import { useWorkspacePlanPrices } from '~/lib/billing/composables/prices'
|
||||
import { formatPrice } from '~/lib/billing/helpers/prices'
|
||||
import { formatPrice } from '~/lib/billing/helpers/plan'
|
||||
|
||||
const props = defineProps<{
|
||||
plan: PaidWorkspacePlansOld
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { isInteger } from 'lodash-es'
|
||||
import { WorkspacePlans } from '@speckle/shared'
|
||||
|
||||
export const formatPrice = (price?: { amount: number; currencySymbol: string }) => {
|
||||
if (!price) return ''
|
||||
return `${price.currencySymbol}${
|
||||
isInteger(price.amount) ? price.amount : price.amount.toFixed(2)
|
||||
}`
|
||||
}
|
||||
|
||||
// Internal plan names dont match the names we use in the product
|
||||
export const formatName = (plan?: WorkspacePlans) => {
|
||||
if (!plan) return ''
|
||||
|
||||
const formattedPlanNames: Record<WorkspacePlans, string> = {
|
||||
[WorkspacePlans.Unlimited]: 'Unlimited',
|
||||
[WorkspacePlans.Academia]: 'Academia',
|
||||
[WorkspacePlans.StarterInvoiced]: 'Starter (invoiced)',
|
||||
[WorkspacePlans.PlusInvoiced]: 'Plus (Invoiced)',
|
||||
[WorkspacePlans.BusinessInvoiced]: 'Business (Invoiced)',
|
||||
[WorkspacePlans.Starter]: 'Starter',
|
||||
[WorkspacePlans.Plus]: 'Plus',
|
||||
[WorkspacePlans.Business]: 'Business',
|
||||
[WorkspacePlans.Free]: 'Free',
|
||||
[WorkspacePlans.Team]: 'Starter',
|
||||
[WorkspacePlans.Pro]: 'Business'
|
||||
}
|
||||
return formattedPlanNames[plan]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { isInteger } from 'lodash-es'
|
||||
|
||||
export const formatPrice = (price?: { amount: number; currencySymbol: string }) => {
|
||||
if (!price) return ''
|
||||
return `${price.currencySymbol}${
|
||||
isInteger(price.amount) ? price.amount : price.amount.toFixed(2)
|
||||
}`
|
||||
}
|
||||
@@ -78,7 +78,6 @@ export const useWorkspacePlan = (slug: string) => {
|
||||
const intervalIsYearly = computed(
|
||||
() => billingInterval.value === BillingInterval.Yearly
|
||||
)
|
||||
|
||||
// TODO: Replace with value from API call, this a placeholder value
|
||||
const seatPrice = 15
|
||||
|
||||
@@ -109,6 +108,7 @@ export const useWorkspacePlan = (slug: string) => {
|
||||
billingInterval,
|
||||
intervalIsYearly,
|
||||
totalCostFormatted,
|
||||
statusIsCancelationScheduled
|
||||
statusIsCancelationScheduled,
|
||||
subscription
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +38,12 @@ export type PaidWorkspacePlans =
|
||||
|
||||
export const UnpaidWorkspacePlans = <const>{
|
||||
// Old
|
||||
Unlimited: 'unlimited',
|
||||
Academia: 'academia',
|
||||
StarterInvoiced: 'starterInvoiced',
|
||||
PlusInvoiced: 'plusInvoiced',
|
||||
BusinessInvoiced: 'businessInvoiced',
|
||||
// New
|
||||
Unlimited: 'unlimited',
|
||||
Academia: 'academia',
|
||||
Free: 'free'
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user