diff --git a/src/lib/components/billing/estimatedTotalBox.svelte b/src/lib/components/billing/estimatedTotalBox.svelte index 303e8de23..3ff31041c 100644 --- a/src/lib/components/billing/estimatedTotalBox.svelte +++ b/src/lib/components/billing/estimatedTotalBox.svelte @@ -11,7 +11,6 @@ export let couponData: Partial; 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); @@ -42,8 +41,8 @@

{formatCurrency(currentPlan.price)}

-

Additional seats ({collaborators?.length})

-

+

Additional seats ({collaborators?.length})

+

{formatCurrency(extraSeatsCost)}

diff --git a/src/lib/components/billing/index.ts b/src/lib/components/billing/index.ts index 266d217d8..b8aac8ada 100644 --- a/src/lib/components/billing/index.ts +++ b/src/lib/components/billing/index.ts @@ -6,4 +6,3 @@ 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'; diff --git a/src/lib/components/billing/planComparisonBox.svelte b/src/lib/components/billing/planComparisonBox.svelte index e297d8e75..ce1abe329 100644 --- a/src/lib/components/billing/planComparisonBox.svelte +++ b/src/lib/components/billing/planComparisonBox.svelte @@ -1,38 +1,30 @@ - -
- - (selectedTab = BillingPlan.FREE)}> - {tierFree.name} - - (selectedTab = BillingPlan.PRO)}> - {tierPro.name} - - (selectedTab = BillingPlan.SCALE)}> - {tierScale.name} - - -
+ + + (selectedTab = 'tier-0')}> + Free + + (selectedTab = 'tier-1')}> + Pro + +
- {#if selectedTab === BillingPlan.FREE} + {#if selectedTab === 'tier-0'}

{plan.name} plan

{#if downgrade}
    @@ -94,50 +86,16 @@
{/if} - {:else if selectedTab === BillingPlan.PRO} + {:else if selectedTab === 'tier-1'}

{plan.name} plan

-

Everything in the Free plan, plus:

-
    +
      +
    • Everything in the Free plan, plus:
    • Unlimited databases, buckets, functions
    • {plan.bandwidth}GB bandwidth
    • {plan.storage}GB storage
    • {formatNum(plan.executions)} executions
    • Email support
    - {:else if selectedTab === BillingPlan.SCALE} -

    {plan.name} plan

    -

    Everything in the Pro plan, plus:

    -
      -
    • Unlimited seats
    • -
    • Organization roles Coming soon
    • -
    • SOC-2, HIPAA compliance
    • -
    • SSO Coming soon
    • -
    • Priority support
    • -
    {/if}
- - diff --git a/src/lib/components/billing/planExcess.svelte b/src/lib/components/billing/planExcess.svelte index 352f0b29b..10d0842d7 100644 --- a/src/lib/components/billing/planExcess.svelte +++ b/src/lib/components/billing/planExcess.svelte @@ -23,7 +23,6 @@ import { tooltip } from '$lib/actions/tooltip'; export let tier: Tier; - export let members: number; const plan = $plansInfo?.get(tier); let excess: { @@ -42,7 +41,7 @@ $organization.billingCurrentInvoiceDate, new Date().toISOString() ); - excess = calculateExcess(usage, plan, members); + excess = calculateExcess(usage, plan, $organization); showExcess = Object.values(excess).some((value) => value > 0); }); @@ -99,8 +98,7 @@

- {humanFileSize(excess?.storage).value} - {humanFileSize(excess?.storage).unit} + {humanFileSize(excess?.storage)}

diff --git a/src/lib/components/billing/planSelection.svelte b/src/lib/components/billing/planSelection.svelte deleted file mode 100644 index c2af34653..000000000 --- a/src/lib/components/billing/planSelection.svelte +++ /dev/null @@ -1,92 +0,0 @@ - - -{#if billingPlan} - -{/if} diff --git a/src/lib/stores/billing.ts b/src/lib/stores/billing.ts index ce7adccf7..5e54db955 100644 --- a/src/lib/stores/billing.ts +++ b/src/lib/stores/billing.ts @@ -150,7 +150,7 @@ export const tierPro: TierData = { }; export const tierScale: TierData = { name: 'Scale', - description: 'For scaling teams and agencies that need dedicated support.' + description: 'For scaling teams that need dedicated support.' }; export const showUsageRatesModal = writable(false); @@ -422,14 +422,14 @@ export const upgradeURL = derived( export const hideBillingHeaderRoutes = ['/console/create-organization', '/console/account']; -export function calculateExcess(usage: OrganizationUsage, plan: Plan, members: number) { +export function calculateExcess(usage: OrganizationUsage, plan: Plan, org: Organization) { 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(members, plan.members) + members: calculateResourceSurplus(org.total, plan.members) }; } diff --git a/src/lib/stores/campaigns.ts b/src/lib/stores/campaigns.ts index 315a4230f..3f7866b9e 100644 --- a/src/lib/stores/campaigns.ts +++ b/src/lib/stores/campaigns.ts @@ -1,14 +1,11 @@ //campaign welcome and startup -import { BillingPlan } from '$lib/constants'; - export type CampaignData = { title: string; description: string; template: 'card' | 'review'; data?: Record; onlyNewOrgs?: boolean; - plan?: BillingPlan; footer?: boolean; }; @@ -25,7 +22,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 Scale account to join." + "We're excited to have you on board. Add the coupon code to your Appwrite Pro account to join." }) .set('RenderATL2024', { template: 'card', @@ -55,11 +52,10 @@ 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 Scale account to join.", + "We're excited to have you on board. Add your credit code to your Appwrite Pro account to join.", onlyNewOrgs: true, - plan: BillingPlan.SCALE, data: { - cta: 'Get everything out of Cloud with Scale', + cta: 'Get everything out of Cloud with Pro', claimed: 'Your credits will be valid for 12 months.', unclaimed: 'Apply your code to join the Startups program.', reviews: [ diff --git a/src/lib/stores/organization.ts b/src/lib/stores/organization.ts index f0f363d56..2beb13b03 100644 --- a/src/lib/stores/organization.ts +++ b/src/lib/stores/organization.ts @@ -19,7 +19,7 @@ export type Organization = Models.Team> & { billingAddressId?: string; amount: number; billingTaxId?: string; - billingPlanDowngrade?: Tier; + billingPlanDowngrade?: string; }; export type OrganizationList = { diff --git a/src/routes/console/apply-credit/+page.svelte b/src/routes/console/apply-credit/+page.svelte index 231c8820f..c9f0db557 100644 --- a/src/routes/console/apply-credit/+page.svelte +++ b/src/routes/console/apply-credit/+page.svelte @@ -70,7 +70,6 @@ let coupon: string; let couponData = data?.couponData; let campaign = campaigns.get(data?.couponData?.campaign ?? data?.campaign); - let billingPlan = BillingPlan.PRO; onMount(async () => { await loadPaymentMethods(); @@ -81,9 +80,6 @@ selectedOrgId = $page.url.searchParams.get('org'); canSelectOrg = false; } - if (campaign?.plan) { - billingPlan = campaign.plan; - } }); async function loadPaymentMethods() { @@ -105,15 +101,15 @@ org = await sdk.forConsole.billing.createOrganization( newOrgId, name, - billingPlan, + BillingPlan.PRO, paymentMethodId ); } // Upgrade existing org - else if (selectedOrg?.billingPlan === BillingPlan.FREE) { + else if (selectedOrg?.billingPlan !== BillingPlan.PRO) { org = await sdk.forConsole.billing.updatePlan( selectedOrg.$id, - billingPlan, + BillingPlan.PRO, paymentMethodId, null ); @@ -274,7 +270,7 @@ {/if} - {#if selectedOrg?.billingPlan !== BillingPlan.FREE} + {#if selectedOrg?.billingPlan === BillingPlan.PRO}
(org as Organization)?.billingPlan === BillingPlan.FREE ); const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$/i; @@ -174,7 +175,9 @@ } } - $: if (billingPlan !== BillingPlan.FREE) { + $: freePlan = $plansInfo.get(BillingPlan.FREE); + $: proPlan = $plansInfo.get(BillingPlan.PRO); + $: if (billingPlan === BillingPlan.PRO) { loadPaymentMethods(); } @@ -202,9 +205,53 @@ For more details on our plans, visit our .

- - {#if billingPlan !== BillingPlan.FREE} - +
    +
  • + + +
    +

    + {tierFree.name} +

    +

    {tierFree.description}

    +

    + {formatCurrency(freePlan?.price ?? 0)} +

    +
    +
    +
    +
  • + +
  • + + +
    +

    + {tierPro.name} +

    +

    + {tierPro.description} +

    +

    + {formatCurrency(proPlan?.price ?? 0)} per member/month + usage +

    +
    +
    +
    +
  • +
+ {#if billingPlan === BillingPlan.PRO} + - Your organization will change to a {tierToPlan($organization?.billingPlanDowngrade) - .name} plan once your current billing cycle ends and your invoice is paid on {toLocaleDate( + Your organization will change to a {tierToPlan(BillingPlan.FREE).name} plan once your current + billing cycle ends and your invoice is paid on {toLocaleDate( $organization.billingNextInvoiceDate )}. diff --git a/src/routes/console/organization-[organization]/billing/planSummary.svelte b/src/routes/console/organization-[organization]/billing/planSummary.svelte index 04d1aa258..81edb295e 100644 --- a/src/routes/console/organization-[organization]/billing/planSummary.svelte +++ b/src/routes/console/organization-[organization]/billing/planSummary.svelte @@ -58,7 +58,7 @@ {isTrial ? formatCurrency(0) : formatCurrency(currentPlan?.price)} - {#if $organization?.billingPlan !== BillingPlan.FREE && extraUsage > 0} + {#if $organization?.billingPlan !== BillingPlan.FREE && extraUsage} Add-ons $organization.billingPlan; $: isDowngrade = billingPlan < $organization.billingPlan; - $: if (billingPlan !== BillingPlan.FREE) { + $: freePlan = $plansInfo.get(BillingPlan.FREE); + $: proPlan = $plansInfo.get(BillingPlan.PRO); + $: if (billingPlan === BillingPlan.PRO) { loadPaymentMethods(); } $: isButtonDisabled = $organization.billingPlan === billingPlan; @@ -264,37 +261,72 @@ For more details on our plans, visit our .

- - - {#if isDowngrade} - {#if billingPlan === BillingPlan.FREE} - - {:else if billingPlan === BillingPlan.PRO && $organization.billingPlan === BillingPlan.SCALE} - {@const extraMembers = collaborators?.length ?? 0} - - - Your monthly payments will be adjusted for the Pro plan - - After switching plans, - you will be charged {formatCurrency( - extraMembers * - ($plansInfo?.get(billingPlan)?.addons?.member?.price ?? 0) - )} monthly for {extraMembers} team members. This will be reflected in - your next invoice. - - {/if} + {#if anyOrgFree && billingPlan === BillingPlan.PRO} + + You are limited to one {tierToPlan(BillingPlan.FREE).name} organization per account. + Consider upgrading or deleting . + {/if} - - {#if billingPlan !== BillingPlan.FREE && $organization.billingPlan === BillingPlan.FREE} +
    +
  • + + +
    +

    + {tierFree.name} + {#if $organization.billingPlan === BillingPlan.FREE} + Current plan + {/if} +

    +

    {tierFree.description}

    +

    + {formatCurrency(freePlan?.price ?? 0)} +

    +
    +
    +
    +
  • + +
  • + + +
    +

    + {tierPro.name} + {#if $organization.billingPlan === BillingPlan.PRO} + Current plan + {/if} +

    +

    + {tierPro.description} +

    +

    + {formatCurrency(proPlan?.price ?? 0)} per member/month + usage +

    +
    +
    +
    +
  • +
+ {#if isDowngrade} + + {/if} + {#if billingPlan === BillingPlan.PRO && $organization.billingPlan !== BillingPlan.PRO} {/if} {/if} - {#if isDowngrade} + {#if !isUpgrade && billingPlan === BillingPlan.FREE && $organization.billingPlan !== BillingPlan.FREE} - {#if billingPlan !== BillingPlan.FREE && $organization.billingPlan !== billingPlan} + {#if billingPlan !== BillingPlan.FREE && $organization.billingPlan !== BillingPlan.PRO} + bind:billingBudget /> {:else} {/if} diff --git a/src/routes/console/organization-[organization]/change-plan/+page.ts b/src/routes/console/organization-[organization]/change-plan/+page.ts index b10332405..cde580678 100644 --- a/src/routes/console/organization-[organization]/change-plan/+page.ts +++ b/src/routes/console/organization-[organization]/change-plan/+page.ts @@ -2,8 +2,8 @@ import { Dependencies } from '$lib/constants'; export const load = async ({ depends, parent }) => { const { members } = await parent(); - depends(Dependencies.ORGANIZATION); + return { members }; diff --git a/src/routes/console/organization-[organization]/createMember.svelte b/src/routes/console/organization-[organization]/createMember.svelte index c3195a1f2..a0c4ff0dc 100644 --- a/src/routes/console/organization-[organization]/createMember.svelte +++ b/src/routes/console/organization-[organization]/createMember.svelte @@ -59,12 +59,14 @@ {#if isCloud} - {#if $organization?.billingPlan === BillingPlan.PRO} - + + {#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} You can add unlimited organization members on the {plan.name} plan for {formatCurrency(plan.addons.member.price)} each per billing period. - - {/if} + {/if} + {/if}