Merge pull request #1196 from appwrite/feat-scale-plan

feat: scale plan
This commit is contained in:
Steven Nguyen
2024-08-05 10:46:26 -07:00
committed by GitHub
17 changed files with 244 additions and 174 deletions
@@ -11,6 +11,7 @@
export let couponData: Partial<Coupon>;
export let billingBudget: number;
export let fixedCoupon = false; // If true, the coupon cannot be removed
export let isDowngrade = false;
const today = new Date();
const billingPayDate = new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000);
@@ -41,8 +42,8 @@
<p class="text">{formatCurrency(currentPlan.price)}</p>
</span>
<span class="u-flex u-main-space-between">
<p class="text">Additional seats ({collaborators?.length})</p>
<p class="text">
<p class="text" class:u-bold={isDowngrade}>Additional seats ({collaborators?.length})</p>
<p class="text" class:u-bold={isDowngrade}>
{formatCurrency(extraSeatsCost)}
</p>
</span>
+1
View File
@@ -6,3 +6,4 @@ export { default as EstimatedTotalBox } from './estimatedTotalBox.svelte';
export { default as PlanComparisonBox } from './planComparisonBox.svelte';
export { default as EmptyCardCloud } from './emptyCardCloud.svelte';
export { default as CreditsApplied } from './creditsApplied.svelte';
export { default as PlanSelection } from './planSelection.svelte';
@@ -1,30 +1,38 @@
<script lang="ts">
import { BillingPlan } from '$lib/constants';
import { formatNum } from '$lib/helpers/string';
import { plansInfo } from '$lib/stores/billing';
import { plansInfo, tierFree, tierPro, tierScale, type Tier } from '$lib/stores/billing';
import { Card, SecondaryTabs, SecondaryTabsItem } from '..';
let selectedTab: 'tier-0' | 'tier-1' = 'tier-0';
let selectedTab: Tier = BillingPlan.FREE;
export let downgrade = false;
$: plan = $plansInfo.get(selectedTab);
</script>
<Card>
<SecondaryTabs stretch>
<SecondaryTabsItem
disabled={selectedTab === 'tier-0'}
on:click={() => (selectedTab = 'tier-0')}>
Free
</SecondaryTabsItem>
<SecondaryTabsItem
disabled={selectedTab === 'tier-1'}
on:click={() => (selectedTab = 'tier-1')}>
Pro
</SecondaryTabsItem>
</SecondaryTabs>
<Card style="--card-padding: 1.5rem">
<div class="comparison-box">
<SecondaryTabs stretch>
<SecondaryTabsItem
disabled={selectedTab === BillingPlan.FREE}
on:click={() => (selectedTab = BillingPlan.FREE)}>
{tierFree.name}
</SecondaryTabsItem>
<SecondaryTabsItem
disabled={selectedTab === BillingPlan.PRO}
on:click={() => (selectedTab = BillingPlan.PRO)}>
{tierPro.name}
</SecondaryTabsItem>
<SecondaryTabsItem
disabled={selectedTab === BillingPlan.SCALE}
on:click={() => (selectedTab = BillingPlan.SCALE)}>
{tierScale.name}
</SecondaryTabsItem>
</SecondaryTabs>
</div>
<div class="u-margin-block-start-24">
{#if selectedTab === 'tier-0'}
{#if selectedTab === BillingPlan.FREE}
<h3 class="u-bold body-text-1">{plan.name} plan</h3>
{#if downgrade}
<ul class="u-margin-block-start-8 list u-gap-4 u-small">
@@ -86,16 +94,50 @@
</li>
</ul>
{/if}
{:else if selectedTab === 'tier-1'}
{:else if selectedTab === BillingPlan.PRO}
<h3 class="u-bold body-text-1">{plan.name} plan</h3>
<ul class="un-order-list u-margin-block-start-8">
<li>Everything in the Free plan, plus:</li>
<p class="u-margin-block-start-8">Everything in the Free plan, plus:</p>
<ul class="un-order-list u-margin-inline-start-4">
<li>Unlimited databases, buckets, functions</li>
<li>{plan.bandwidth}GB bandwidth</li>
<li>{plan.storage}GB storage</li>
<li>{formatNum(plan.executions)} executions</li>
<li>Email support</li>
</ul>
{:else if selectedTab === BillingPlan.SCALE}
<h3 class="u-bold body-text-1">{plan.name} plan</h3>
<p class="u-margin-block-start-8">Everything in the Pro plan, plus:</p>
<ul class="un-order-list u-margin-inline-start-4">
<li>Unlimited seats</li>
<li>Organization roles <span class="inline-tag">Coming soon</span></li>
<li>SOC-2, HIPAA compliance</li>
<li>SSO <span class="inline-tag">Coming soon</span></li>
<li>Priority support</li>
</ul>
{/if}
</div>
</Card>
<style lang="scss">
.comparison-box {
border-radius: var(--border-radius-small);
background: hsl(var(--color-neutral-5));
}
:global(.theme-dark) .comparison-box {
background: hsl(var(--color-neutral-85));
}
.comparison-box :global(.secondary-tabs-button:where(:disabled)) {
background: hsl(var(--color-neutral-0));
border: 1px solid hsl(var(--color-neutral-10));
}
:global(.theme-dark) .comparison-box :global(.secondary-tabs-button:where(:disabled)) {
background: hsl(var(--color-neutral-80));
border: 1px solid hsl(var(--color-neutral-85));
}
.inline-tag {
line-height: 140%;
font-weight: 500;
}
</style>
+4 -2
View File
@@ -23,6 +23,7 @@
import { tooltip } from '$lib/actions/tooltip';
export let tier: Tier;
export let members: number;
const plan = $plansInfo?.get(tier);
let excess: {
@@ -41,7 +42,7 @@
$organization.billingCurrentInvoiceDate,
new Date().toISOString()
);
excess = calculateExcess(usage, plan, $organization);
excess = calculateExcess(usage, plan, members);
showExcess = Object.values(excess).some((value) => value > 0);
});
</script>
@@ -98,7 +99,8 @@
<TableCell title="excess">
<p class="u-color-text-danger">
<span class="icon-arrow-up" />
{humanFileSize(excess?.storage)}
{humanFileSize(excess?.storage).value}
{humanFileSize(excess?.storage).unit}
</p>
</TableCell>
</TableRow>
@@ -0,0 +1,92 @@
<script lang="ts">
import { BillingPlan } from '$lib/constants';
import { formatCurrency } from '$lib/helpers/numbers';
import { plansInfo, tierFree, tierPro, tierScale, type Tier } from '$lib/stores/billing';
import { organization } from '$lib/stores/organization';
import { LabelCard } from '..';
export let billingPlan: Tier;
export let anyOrgFree = false;
export let isNewOrg = false;
let classes: string = '';
export { classes as class };
$: freePlan = $plansInfo.get(BillingPlan.FREE);
$: proPlan = $plansInfo.get(BillingPlan.PRO);
$: scalePlan = $plansInfo.get(BillingPlan.SCALE);
</script>
{#if billingPlan}
<ul class="u-flex u-flex-vertical u-gap-16 u-margin-block-start-8 {classes}">
<li>
<LabelCard
name="plan"
bind:group={billingPlan}
disabled={anyOrgFree}
value={BillingPlan.FREE}
tooltipShow={anyOrgFree}
tooltipText="You are limited to 1 Free organization per account."
padding={1.5}>
<svelte:fragment slot="custom" let:disabled>
<div
class="u-flex u-flex-vertical u-gap-4 u-width-full-line"
class:u-opacity-50={disabled}>
<h4 class="body-text-2 u-bold">
{tierFree.name}
{#if $organization?.billingPlan === BillingPlan.FREE && !isNewOrg}
<span class="inline-tag">Current plan</span>
{/if}
</h4>
<p class="u-color-text-offline u-small">
{tierFree.description}
</p>
<p>
{formatCurrency(freePlan?.price ?? 0)}
</p>
</div>
</svelte:fragment>
</LabelCard>
</li>
<li>
<LabelCard name="plan" bind:group={billingPlan} value={BillingPlan.PRO} padding={1.5}>
<svelte:fragment slot="custom">
<div class="u-flex u-flex-vertical u-gap-4 u-width-full-line">
<h4 class="body-text-2 u-bold">
{tierPro.name}
{#if $organization?.billingPlan === BillingPlan.PRO && !isNewOrg}
<span class="inline-tag">Current plan</span>
{/if}
</h4>
<p class="u-color-text-offline u-small">
{tierPro.description}
</p>
<p>
{formatCurrency(proPlan?.price ?? 0)} per member/month + usage
</p>
</div>
</svelte:fragment>
</LabelCard>
</li>
<li>
<LabelCard name="plan" bind:group={billingPlan} value={BillingPlan.SCALE} padding={1.5}>
<svelte:fragment slot="custom">
<div class="u-flex u-flex-vertical u-gap-4 u-width-full-line">
<h4 class="body-text-2 u-bold">
{tierScale.name}
{#if $organization?.billingPlan === BillingPlan.SCALE && !isNewOrg}
<span class="inline-tag">Current plan</span>
{/if}
</h4>
<p class="u-color-text-offline u-small">
{tierScale.description}
</p>
<p>
{formatCurrency(scalePlan?.price ?? 0)} per month + usage
</p>
</div>
</svelte:fragment>
</LabelCard>
</li>
</ul>
{/if}
+3 -3
View File
@@ -150,7 +150,7 @@ export const tierPro: TierData = {
};
export const tierScale: TierData = {
name: 'Scale',
description: 'For scaling teams that need dedicated support.'
description: 'For scaling teams and agencies that need dedicated support.'
};
export const showUsageRatesModal = writable<boolean>(false);
@@ -422,14 +422,14 @@ export const upgradeURL = derived(
export const hideBillingHeaderRoutes = ['/console/create-organization', '/console/account'];
export function calculateExcess(usage: OrganizationUsage, plan: Plan, org: Organization) {
export function calculateExcess(usage: OrganizationUsage, plan: Plan, members: number) {
const totBandwidth = usage?.bandwidth?.length > 0 ? last(usage.bandwidth).value : 0;
return {
bandwidth: calculateResourceSurplus(totBandwidth, plan.bandwidth),
storage: calculateResourceSurplus(usage?.storageTotal, plan.storage, 'GB'),
users: calculateResourceSurplus(usage?.usersTotal, plan.users),
executions: calculateResourceSurplus(usage?.executionsTotal, plan.executions, 'GB'),
members: calculateResourceSurplus(org.total, plan.members)
members: calculateResourceSurplus(members, plan.members)
};
}
+7 -3
View File
@@ -1,11 +1,14 @@
//campaign welcome and startup
import { BillingPlan } from '$lib/constants';
export type CampaignData = {
title: string;
description: string;
template: 'card' | 'review';
data?: Record<string, unknown>;
onlyNewOrgs?: boolean;
plan?: BillingPlan;
footer?: boolean;
};
@@ -22,7 +25,7 @@ campaigns
template: 'card',
title: 'Welcome to the Startups program!',
description:
"We're excited to have you on board. Add the coupon code to your Appwrite Pro account to join."
"We're excited to have you on board. Add the coupon code to your Appwrite Scale account to join."
})
.set('RenderATL2024', {
template: 'card',
@@ -52,10 +55,11 @@ campaigns
template: 'review',
title: 'Welcome to the Startups program',
description:
"We're excited to have you on board. Add your credit code to your Appwrite Pro account to join.",
"We're excited to have you on board. Add your credit code to your Appwrite Scale account to join.",
onlyNewOrgs: true,
plan: BillingPlan.SCALE,
data: {
cta: 'Get everything out of Cloud with Pro',
cta: 'Get everything out of Cloud with Scale',
claimed: 'Your credits will be valid for 12 months.',
unclaimed: 'Apply your code to join the Startups program.',
reviews: [
+1 -1
View File
@@ -19,7 +19,7 @@ export type Organization = Models.Team<Record<string, unknown>> & {
billingAddressId?: string;
amount: number;
billingTaxId?: string;
billingPlanDowngrade?: string;
billingPlanDowngrade?: Tier;
};
export type OrganizationList = {
+8 -4
View File
@@ -70,6 +70,7 @@
let coupon: string;
let couponData = data?.couponData;
let campaign = campaigns.get(data?.couponData?.campaign ?? data?.campaign);
let billingPlan = BillingPlan.PRO;
onMount(async () => {
await loadPaymentMethods();
@@ -80,6 +81,9 @@
selectedOrgId = $page.url.searchParams.get('org');
canSelectOrg = false;
}
if (campaign?.plan) {
billingPlan = campaign.plan;
}
});
async function loadPaymentMethods() {
@@ -101,15 +105,15 @@
org = await sdk.forConsole.billing.createOrganization(
newOrgId,
name,
BillingPlan.PRO,
billingPlan,
paymentMethodId
);
}
// Upgrade existing org
else if (selectedOrg?.billingPlan !== BillingPlan.PRO) {
else if (selectedOrg?.billingPlan === BillingPlan.FREE) {
org = await sdk.forConsole.billing.updatePlan(
selectedOrg.$id,
BillingPlan.PRO,
billingPlan,
paymentMethodId,
null
);
@@ -270,7 +274,7 @@
</div>
</div>
{/if}
{#if selectedOrg?.billingPlan === BillingPlan.PRO}
{#if selectedOrg?.billingPlan !== BillingPlan.FREE}
<section
class="card u-margin-block-start-24"
style:--p-card-padding="1.5rem"
@@ -3,16 +3,15 @@
import { base } from '$app/paths';
import { page } from '$app/stores';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { LabelCard } from '$lib/components';
import {
EstimatedTotalBox,
PlanComparisonBox,
PlanSelection,
SelectPaymentMethod
} from '$lib/components/billing';
import ValidateCreditModal from '$lib/components/billing/validateCreditModal.svelte';
import { BillingPlan, Dependencies } from '$lib/constants';
import { Button, Form, FormList, InputTags, InputText, Label } from '$lib/elements/forms';
import { formatCurrency } from '$lib/helpers/numbers';
import {
WizardSecondaryContainer,
WizardSecondaryContent,
@@ -20,7 +19,7 @@
WizardSecondaryHeader
} from '$lib/layout';
import type { Coupon, PaymentList } from '$lib/sdk/billing';
import { plansInfo, tierFree, tierPro, tierToPlan } from '$lib/stores/billing';
import { tierToPlan } from '$lib/stores/billing';
import { addNotification } from '$lib/stores/notifications';
import { organizationList, type Organization } from '$lib/stores/organization';
import { sdk } from '$lib/stores/sdk';
@@ -28,7 +27,7 @@
import { onMount } from 'svelte';
import { writable } from 'svelte/store';
$: anyOrgFree = $organizationList.teams?.find(
$: anyOrgFree = $organizationList.teams?.some(
(org) => (org as Organization)?.billingPlan === BillingPlan.FREE
);
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$/i;
@@ -175,9 +174,7 @@
}
}
$: freePlan = $plansInfo.get(BillingPlan.FREE);
$: proPlan = $plansInfo.get(BillingPlan.PRO);
$: if (billingPlan === BillingPlan.PRO) {
$: if (billingPlan !== BillingPlan.FREE) {
loadPaymentMethods();
}
</script>
@@ -205,53 +202,9 @@
For more details on our plans, visit our
<Button href="https://appwrite.io/pricing" external link>pricing page</Button>.
</p>
<ul
class="u-flex u-gap-16 u-margin-block-start-8"
style="--p-grid-item-size:16em; --p-grid-item-size-small-screens:16rem; --grid-gap: 1rem;">
<li class="u-flex-basis-50-percent">
<LabelCard
name="plan"
bind:group={billingPlan}
value="tier-0"
disabled={!!anyOrgFree}
tooltipShow={!!anyOrgFree}
tooltipText="You are limited to 1 Free organization per account.">
<svelte:fragment slot="custom" let:disabled>
<div
class="u-flex u-flex-vertical u-gap-4 u-width-full-line"
class:u-opacity-50={disabled}>
<h4 class="body-text-2 u-bold">
{tierFree.name}
</h4>
<p class="u-color-text-gray u-small">{tierFree.description}</p>
<p>
{formatCurrency(freePlan?.price ?? 0)}
</p>
</div>
</svelte:fragment>
</LabelCard>
</li>
<li class="u-flex-basis-50-percent">
<LabelCard name="plan" bind:group={billingPlan} value="tier-1">
<svelte:fragment slot="custom">
<div class="u-flex u-flex-vertical u-gap-4 u-width-full-line">
<h4 class="body-text-2 u-bold">
{tierPro.name}
</h4>
<p class="u-color-text-gray u-small">
{tierPro.description}
</p>
<p>
{formatCurrency(proPlan?.price ?? 0)} per member/month + usage
</p>
</div>
</svelte:fragment>
</LabelCard>
</li>
</ul>
{#if billingPlan === BillingPlan.PRO}
<FormList class="u-margin-block-start-16">
<PlanSelection bind:billingPlan {anyOrgFree} isNewOrg />
{#if billingPlan !== BillingPlan.FREE}
<FormList class="u-margin-block-start-24">
<InputTags
bind:tags={collaborators}
label="Invite members by email"
@@ -32,6 +32,10 @@
{
value: BillingPlan.PRO,
label: `${tierToPlan(BillingPlan.PRO).name} - ${formatCurrency($plansInfo.get(BillingPlan.PRO).price)}/month + add-ons`
},
{
value: BillingPlan.SCALE,
label: `${tierToPlan(BillingPlan.SCALE).name} - ${formatCurrency($plansInfo.get(BillingPlan.SCALE).price)}/month + usage`
}
]
: [];
@@ -111,8 +111,8 @@
{/if}
{#if $organization?.billingPlanDowngrade}
<Alert type="info" class="common-section">
Your organization will change to a {tierToPlan(BillingPlan.FREE).name} plan once your current
billing cycle ends and your invoice is paid on {toLocaleDate(
Your organization will change to a {tierToPlan($organization?.billingPlanDowngrade)
.name} plan once your current billing cycle ends and your invoice is paid on {toLocaleDate(
$organization.billingNextInvoiceDate
)}.
</Alert>
@@ -58,7 +58,7 @@
{isTrial ? formatCurrency(0) : formatCurrency(currentPlan?.price)}
</div>
</CollapsibleItem>
{#if $organization?.billingPlan !== BillingPlan.FREE && extraUsage}
{#if $organization?.billingPlan !== BillingPlan.FREE && extraUsage > 0}
<CollapsibleItem isInfo gap={8}>
<svelte:fragment slot="beforetitle">
<span class="body-text-2"><b>Add-ons</b></span><span class="inline-tag"
@@ -3,13 +3,14 @@
import { base } from '$app/paths';
import { page } from '$app/stores';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Alert, LabelCard } from '$lib/components';
import { Alert } from '$lib/components';
import {
EstimatedTotalBox,
PlanComparisonBox,
SelectPaymentMethod
} from '$lib/components/billing';
import PlanExcess from '$lib/components/billing/planExcess.svelte';
import PlanSelection from '$lib/components/billing/planSelection.svelte';
import ValidateCreditModal from '$lib/components/billing/validateCreditModal.svelte';
import { BillingPlan, Dependencies, feedbackDowngradeOptions } from '$lib/constants';
import {
@@ -21,7 +22,7 @@
InputTextarea,
Label
} from '$lib/elements/forms';
import { formatCurrency } from '$lib/helpers/numbers';
import { formatCurrency } from '$lib/helpers/numbers.js';
import {
WizardSecondaryContainer,
WizardSecondaryContent,
@@ -29,7 +30,7 @@
WizardSecondaryHeader
} from '$lib/layout';
import { type Coupon, type PaymentList } from '$lib/sdk/billing';
import { plansInfo, tierFree, tierPro, tierToPlan, type Tier } from '$lib/stores/billing';
import { plansInfo, tierToPlan, type Tier } from '$lib/stores/billing';
import { addNotification } from '$lib/stores/notifications';
import { organization, organizationList, type Organization } from '$lib/stores/organization';
import { sdk } from '$lib/stores/sdk';
@@ -93,7 +94,11 @@
billingPlan = plan as BillingPlan;
}
}
billingPlan = BillingPlan.PRO;
if ($organization?.billingPlan === BillingPlan.SCALE) {
billingPlan = BillingPlan.SCALE;
} else {
billingPlan = BillingPlan.PRO;
}
});
async function loadPaymentMethods() {
@@ -106,9 +111,9 @@
}
async function handleSubmit() {
if (billingPlan === BillingPlan.FREE) {
if (isDowngrade) {
await downgrade();
} else {
} else if (isUpgrade) {
await upgrade();
}
}
@@ -238,9 +243,7 @@
$: isUpgrade = billingPlan > $organization.billingPlan;
$: isDowngrade = billingPlan < $organization.billingPlan;
$: freePlan = $plansInfo.get(BillingPlan.FREE);
$: proPlan = $plansInfo.get(BillingPlan.PRO);
$: if (billingPlan === BillingPlan.PRO) {
$: if (billingPlan !== BillingPlan.FREE) {
loadPaymentMethods();
}
$: isButtonDisabled = $organization.billingPlan === billingPlan;
@@ -261,72 +264,37 @@
For more details on our plans, visit our
<Button href="https://appwrite.io/pricing" external link>pricing page</Button>.
</p>
{#if anyOrgFree && billingPlan === BillingPlan.PRO}
<Alert type="warning" class="u-margin-block-16">
You are limited to one {tierToPlan(BillingPlan.FREE).name} organization per account.
Consider upgrading or deleting <Button
link
href={`${base}/console/organization-${anyOrgFree.$id}`}
>{anyOrgFree.name}</Button
>.
</Alert>
{/if}
<ul
class="u-flex u-gap-16 u-margin-block-start-8"
class:u-margin-block-start-16={anyOrgFree && billingPlan === BillingPlan.PRO}
style="--p-grid-item-size:16em; --p-grid-item-size-small-screens:16rem; --grid-gap: 1rem;">
<li class="u-flex-basis-50-percent">
<LabelCard
name="plan"
bind:group={billingPlan}
disabled={!!anyOrgFree}
value="tier-0"
tooltipShow={!!anyOrgFree}
tooltipText="You are limited to 1 Free organization per account.">
<svelte:fragment slot="custom" let:disabled>
<div
class="u-flex u-flex-vertical u-gap-4 u-width-full-line"
class:u-opacity-50={disabled}>
<h4 class="body-text-2 u-bold">
{tierFree.name}
{#if $organization.billingPlan === BillingPlan.FREE}
<span class="inline-tag">Current plan</span>
{/if}
</h4>
<p class="u-color-text-gray u-small">{tierFree.description}</p>
<p>
{formatCurrency(freePlan?.price ?? 0)}
</p>
</div>
</svelte:fragment>
</LabelCard>
</li>
<PlanSelection
bind:billingPlan
anyOrgFree={!!anyOrgFree}
class={anyOrgFree && billingPlan !== BillingPlan.FREE
? 'u-margin-block-start-16'
: ''} />
<li class="u-flex-basis-50-percent">
<LabelCard name="plan" bind:group={billingPlan} value="tier-1">
<svelte:fragment slot="custom">
<div class="u-flex u-flex-vertical u-gap-4 u-width-full-line">
<h4 class="body-text-2 u-bold">
{tierPro.name}
{#if $organization.billingPlan === BillingPlan.PRO}
<span class="inline-tag">Current plan</span>
{/if}
</h4>
<p class="u-color-text-gray u-small">
{tierPro.description}
</p>
<p>
{formatCurrency(proPlan?.price ?? 0)} per member/month + usage
</p>
</div>
</svelte:fragment>
</LabelCard>
</li>
</ul>
{#if isDowngrade}
<PlanExcess tier={BillingPlan.FREE} class="u-margin-block-start-24" />
{#if billingPlan === BillingPlan.FREE}
<PlanExcess
tier={BillingPlan.FREE}
class="u-margin-block-start-24"
members={data?.members?.total ?? 0} />
{:else if billingPlan === BillingPlan.PRO && $organization.billingPlan === BillingPlan.SCALE}
{@const extraMembers = collaborators?.length ?? 0}
<Alert type="error" class="u-margin-block-start-24">
<svelte:fragment slot="title">
Your monthly payments will be adjusted for the Pro plan
</svelte:fragment>
After switching plans,
<b
>you will be charged {formatCurrency(
extraMembers *
($plansInfo?.get(billingPlan)?.addons?.member?.price ?? 0)
)} monthly for {extraMembers} team members.</b> This will be reflected in
your next invoice.
</Alert>
{/if}
{/if}
{#if billingPlan === BillingPlan.PRO && $organization.billingPlan !== BillingPlan.PRO}
<!-- Show email input if upgrading from free plan -->
{#if billingPlan !== BillingPlan.FREE && $organization.billingPlan === BillingPlan.FREE}
<FormList class="u-margin-block-start-16">
<InputTags
bind:tags={collaborators}
@@ -348,7 +316,7 @@
</Button>
{/if}
{/if}
{#if !isUpgrade && billingPlan === BillingPlan.FREE && $organization.billingPlan !== BillingPlan.FREE}
{#if isDowngrade}
<FormList class="u-margin-block-start-24">
<InputSelect
id="reason"
@@ -366,12 +334,13 @@
{/if}
</Form>
<svelte:fragment slot="aside">
{#if billingPlan !== BillingPlan.FREE && $organization.billingPlan !== BillingPlan.PRO}
{#if billingPlan !== BillingPlan.FREE && $organization.billingPlan !== billingPlan}
<EstimatedTotalBox
{billingPlan}
{collaborators}
bind:couponData
bind:billingBudget />
bind:billingBudget
{isDowngrade} />
{:else}
<PlanComparisonBox downgrade={isDowngrade} />
{/if}
@@ -2,8 +2,8 @@ import { Dependencies } from '$lib/constants';
export const load = async ({ depends, parent }) => {
const { members } = await parent();
depends(Dependencies.ORGANIZATION);
depends(Dependencies.ORGANIZATION);
return {
members
};
@@ -59,14 +59,12 @@
<Modal title="Invite member" {error} size="big" bind:show={showCreate} onSubmit={create}>
{#if isCloud}
<Alert type="info">
{#if $organization?.billingPlan === BillingPlan.SCALE}
You can add unlimited organization members on the {plan.name} plan at no cost.
{:else if $organization?.billingPlan === BillingPlan.PRO}
{#if $organization?.billingPlan === BillingPlan.PRO}
<Alert type="info">
You can add unlimited organization members on the {plan.name} plan for
<b>{formatCurrency(plan.addons.member.price)} each per billing period</b>.
{/if}
</Alert>
</Alert>
{/if}
{/if}
<FormList>
<InputEmail
@@ -46,7 +46,7 @@
<span
class="icon-info"
use:tooltip={{
content: `You can add unlimited organization members on the ${tierToPlan($organization.billingPlan).name} plan for ${formatCurrency(plan.addons.member.price)} each per billing period.`
content: `You can add unlimited organization members on the ${tierToPlan($organization.billingPlan).name} plan ${$organization.billingPlan === BillingPlan.PRO ? `for ${formatCurrency(plan.addons.member.price)} each per billing period.` : '.'}`
}}></span>
</p>
</div>