Skip to content

Commit

Permalink
Feat: Add mobile pricing plan (#3509)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mikehrn authored Nov 20, 2024
1 parent 5562d41 commit a0fb2ed
Show file tree
Hide file tree
Showing 10 changed files with 318 additions and 179 deletions.
10 changes: 8 additions & 2 deletions packages/frontend-2/components/settings/workspaces/Billing.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<section>
<div class="md:mx-auto pb-6 md:pb-0">
<div class="md:max-w-5xl md:mx-auto pb-6 md:pb-0">
<SettingsSectionHeader title="Billing" text="Your workspace billing details" />
<template v-if="isBillingIntegrationEnabled">
<div class="flex flex-col gap-y-4 md:gap-y-6">
Expand All @@ -14,7 +14,7 @@
<SettingsSectionHeader title="Billing summary" subheading class="pt-4" />
<div class="border border-outline-3 rounded-lg">
<div
class="grid grid-cols-1 md:grid-cols-3 divide-y md:divide-y-0 md:divide-x"
class="grid grid-cols-1 md:grid-cols-3 divide-y divide-outline-3 md:divide-y-0 md:divide-x"
>
<div class="p-5 pt-4 flex flex-col gap-y-1">
<h3 class="text-body-xs text-foreground-2 pb-2">
Expand Down Expand Up @@ -89,6 +89,7 @@
class="pt-6"
:workspace-id="workspaceId"
:current-plan="currentPlan"
:is-admin="isAdmin"
/>
</div>
</template>
Expand All @@ -111,10 +112,12 @@ import {
import { useBillingActions } from '~/lib/billing/composables/actions'
import { pricingPlansConfig } from '~/lib/billing/helpers/constants'
import { Roles } from '@speckle/shared'
graphql(`
fragment SettingsWorkspacesBilling_Workspace on Workspace {
...BillingAlert_Workspace
id
role
plan {
...SettingsWorkspacesBillingPricingTable_WorkspacePlan
name
Expand Down Expand Up @@ -186,6 +189,9 @@ const nextPaymentDue = computed(() =>
: 'Never'
: dayjs().add(30, 'days').format('MMMM D, YYYY')
)
const isAdmin = computed(
() => workspaceResult.value?.workspace.role === Roles.Workspace.Admin
)
onMounted(() => {
const paymentStatusQuery = route.query?.payment_status
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<template>
<table class="w-full flex flex-col">
<thead>
<tr class="w-full flex">
<th class="w-1/4 flex pl-5 pr-6 pt-4 pb-2 font-medium">
<h4>Compare plans</h4>
</th>
<th
v-for="plan in plans"
:key="`desktop-${plan.name}`"
class="w-1/4 px-6 pt-4 pb-2"
:class="[
plan.name === WorkspacePlans.Team
? 'border border-b-0 border-outline-3 bg-foundation-2 rounded-t-lg'
: ''
]"
scope="col"
>
<SettingsWorkspacesBillingPricingTableHeader
:plan="plan"
:is-yearly-plan="isYearlyPlan"
:current-plan="currentPlan"
:workspace-id="workspaceId"
:is-admin="isAdmin"
/>
</th>
</tr>
</thead>
<tbody class="w-full flex flex-col">
<tr v-for="(feature, key, index) in features" :key="key" class="flex">
<th
class="font-normal text-foreground text-body-xs w-1/4 pr-3 pt-1"
scope="row"
>
<div class="border-b border-outline-3 min-h-[42px] pl-5 flex items-center">
{{ feature.name }}
</div>
</th>
<td
v-for="plan in plans"
:key="plan.name"
class="px-3 w-1/4 pt-1"
:class="[
plan.name === WorkspacePlans.Team
? 'border-l border-r border-outline-3 bg-foundation-2'
: '',
plan.name === WorkspacePlans.Team &&
index === Object.values(features).length - 1
? 'pb-6 border-b rounded-b-lg'
: ''
]"
>
<div class="border-b border-outline-3 flex items-center px-3 min-h-[42px]">
<CheckIcon
v-if="plan.features.includes(feature.name as PlanFeaturesList)"
class="w-3 h-3 text-foreground"
/>
</div>
</td>
</tr>
</tbody>
</table>
</template>

<script setup lang="ts">
import type { WorkspacePlan } from '~/lib/common/generated/gql/graphql'
import { WorkspacePlans } from '~/lib/common/generated/gql/graphql'
import { pricingPlansConfig } from '~/lib/billing/helpers/constants'
import type { PlanFeaturesList } from '~/lib/billing/helpers/types'
import { CheckIcon } from '@heroicons/vue/24/outline'
import type { MaybeNullOrUndefined } from '@speckle/shared'
defineProps<{
isYearlyPlan: boolean
currentPlan: MaybeNullOrUndefined<WorkspacePlan>
workspaceId: string
isAdmin: boolean
}>()
const plans = ref(pricingPlansConfig.plans)
const features = ref(pricingPlansConfig.features)
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<template>
<div class="flex flex-col gap-y-1 font-normal">
<h4 class="text-foreground text-body-xs">
Workspace
<span class="capitalize">{{ plan.name }}</span>
</h4>
<p class="text-foreground text-heading">
£{{
isYearlyPlan
? plan.cost.yearly[Roles.Workspace.Member]
: plan.cost.monthly[Roles.Workspace.Member]
}}
per seat/month
</p>
<p class="text-foreground-2 text-body-2xs pt-1">
Billed {{ isYearlyPlan ? 'annually' : 'monthly' }}
</p>
<div class="w-full">
<FormButton
:color="plan.name === WorkspacePlans.Team ? 'primary' : 'outline'"
:disabled="(!hasTrialPlan && !canUpgradeToPlan) || !isAdmin"
class="mt-3"
full-width
@click="onUpgradePlanClick(plan.name)"
>
{{ hasTrialPlan ? 'Subscribe' : 'Upgrade' }} to&nbsp;
<span class="capitalize">{{ plan.name }}</span>
</FormButton>
</div>
</div>
</template>

<script setup lang="ts">
import { type PricingPlan } from '@/lib/billing/helpers/types'
import { Roles } from '@speckle/shared'
import {
type WorkspacePlan,
WorkspacePlanStatuses,
WorkspacePlans,
BillingInterval
} from '~/lib/common/generated/gql/graphql'
import { useBillingActions } from '~/lib/billing/composables/actions'
import type { MaybeNullOrUndefined } from '@speckle/shared'
const props = defineProps<{
plan: PricingPlan
isYearlyPlan: boolean
currentPlan: MaybeNullOrUndefined<WorkspacePlan>
workspaceId: string
isAdmin: boolean
}>()
const { upgradePlanRedirect } = useBillingActions()
const canUpgradeToPlan = computed(() => {
if (!props.currentPlan) return false
const allowedUpgrades: Record<WorkspacePlans, WorkspacePlans[]> = {
[WorkspacePlans.Team]: [WorkspacePlans.Pro, WorkspacePlans.Business],
[WorkspacePlans.Pro]: [WorkspacePlans.Business],
[WorkspacePlans.Business]: [],
[WorkspacePlans.Academia]: [],
[WorkspacePlans.Unlimited]: []
}
return allowedUpgrades[props.currentPlan.name].includes(props.plan.name)
})
const hasTrialPlan = computed(
() => props.currentPlan?.status === WorkspacePlanStatuses.Trial || !props.currentPlan
)
const onUpgradePlanClick = (plan: WorkspacePlans) => {
upgradePlanRedirect({
plan,
cycle: props.isYearlyPlan ? BillingInterval.Yearly : BillingInterval.Monthly,
workspaceId: props.workspaceId
})
}
</script>
Loading

0 comments on commit a0fb2ed

Please sign in to comment.