diff --git a/src/lib/components/billing/couponInput.svelte b/src/lib/components/billing/couponInput.svelte index 482f216f89..369619afed 100644 --- a/src/lib/components/billing/couponInput.svelte +++ b/src/lib/components/billing/couponInput.svelte @@ -1,5 +1,6 @@ @@ -14,7 +15,7 @@ - {#if $$slots.actions} + {#if $$slots.actions && !hideFooter}
diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 393623360b..e20bbfc07f 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -279,345 +279,6 @@ export const eventServices: Array = [ } ]; -export const usageRates = { - 'tier-0': [ - { - id: 'members', - resource: 'Organization members', - amount: 1, - unit: '', - rate: '$20/member' - }, - { id: 'bandwith', resource: 'Bandwidth', amount: 10, unit: 'GB', rate: '$0.04/GB' }, - { id: 'storage', resource: 'Storage', amount: 2, unit: 'GB', rate: '$0.025/GB' }, - { - id: 'executions', - resource: 'Function executions', - amount: 750000, - unit: 'executions', - rate: '$2/1M executions' - }, - { - id: 'users', - resource: 'Active users', - amount: 200000, - unit: 'AU', - rate: '$0.0012/user' - }, - { - id: 'connections', - resource: 'Concurrent connections', - amount: 750, - unit: 'connections', - rate: '$5/1K connections' - } - ], - 'tier-1': [ - { - id: 'members', - resource: 'Organization members', - amount: 'Unlimited', - unit: '', - rate: '$20/member' - }, - { id: 'bandwith', resource: 'Bandwidth', amount: 1, unit: 'TB', rate: '$0.04/GB' }, - { id: 'storage', resource: 'Storage', amount: 150, unit: 'GB', rate: '$0.025/GB' }, - { - id: 'executions', - resource: 'Function executions', - amount: 3500000, - unit: 'executions', - rate: '$2/1M executions' - }, - { - id: 'users', - resource: 'Active users', - amount: 200000, - unit: 'AU', - rate: '$0.0012/user' - }, - { - id: 'connections', - resource: 'Concurrent connections', - amount: 750, - unit: 'connections', - rate: '$5/1K connections' - } - ], - 'tier-2': [ - { - id: 'members', - resource: 'Organization members', - amount: 'Unlimited', - unit: '', - rate: '$20/member' - }, - { id: 'bandwith', resource: 'Bandwidth', amount: 1, unit: 'TB', rate: '$0.04/GB' }, - { id: 'storage', resource: 'Storage', amount: 150, unit: 'GB', rate: '$0.025/GB' }, - { - id: 'executions', - resource: 'Function executions', - amount: 3500000, - unit: 'executions', - rate: '$2/1M executions' - }, - { - id: 'users', - resource: 'Active users', - amount: 200000, - unit: 'AU', - rate: '$0.0012/user' - }, - { - id: 'connections', - resource: 'Concurrent connections', - amount: 750, - unit: 'connections', - rate: '$5/1K connections' - } - ] -}; - -//resources: bandwidth, buckets, file size limit, functions, executions, users,teams,logs, members, platforms, webhooks, databases, connections, messages -export const limitRates = { - 'tier-0': [ - { - id: 'bandwith', - amount: 10, - unit: 'GB' - }, - { - id: 'buckets', - amount: 3, - unit: '' - }, - { - id: 'file-size-limit', - amount: 1, - unit: 'MB' - }, - { - id: 'storage', - amount: 5, - unit: 'GB' - }, - { - id: 'functions', - amount: 1, - unit: '' - }, - { - id: 'executions', - amount: 750000, - unit: 'executions' - }, - { - id: 'users', - amount: 200000, - unit: 'AU' - }, - - { - id: 'teams', - amount: 1, - unit: '' - }, - - { - id: 'logs', - amount: 1, - unit: '' - }, - - { - id: 'members', - amount: 1, - unit: '' - }, - { - id: 'platforms', - amount: 1, - unit: '' - }, - { - id: 'webhooks', - amount: 1, - unit: '' - }, - { - id: 'databases', - amount: 1, - unit: '' - }, - { - id: 'connections', - amount: 1, - unit: '' - }, - { - id: 'messages', - amount: 1, - unit: '' - } - ], - 'tier-1': [ - { - id: 'bandwith', - amount: 10, - unit: 'GB' - }, - { - id: 'buckets', - amount: 1, - unit: '' - }, - { - id: 'file-size-limit', - amount: 5, - unit: 'MB' - }, - { - id: 'storage', - amount: 5, - unit: 'GB' - }, - { - id: 'functions', - amount: 1, - unit: '' - }, - { - id: 'executions', - amount: 750000, - unit: 'executions' - }, - { - id: 'users', - amount: 200000, - unit: 'AU' - }, - - { - id: 'teams', - amount: 1, - unit: '' - }, - { - id: 'logs', - amount: 1, - unit: '' - }, - { - id: 'members', - amount: 1, - unit: '' - }, - { - id: 'platforms', - amount: 1, - unit: '' - }, - { - id: 'webhooks', - amount: 1, - unit: '' - }, - { - id: 'databases', - amount: 1, - unit: '' - }, - { - id: 'connections', - amount: 1, - unit: '' - }, - { - id: 'messages', - amount: 1, - unit: '' - } - ], - 'tier-2': [ - { - id: 'bandwith', - amount: 10, - unit: 'GB' - }, - { - id: 'buckets', - amount: 1, - unit: '' - }, - { - id: 'file-size-limit', - amount: 5, - unit: 'MB' - }, - { - id: 'storage', - amount: 5, - unit: 'GB' - }, - { - id: 'functions', - amount: 1, - unit: '' - }, - { - id: 'executions', - amount: 750000, - unit: 'executions' - }, - { - id: 'users', - amount: 200000, - unit: 'AU' - }, - - { - id: 'teams', - amount: 1, - unit: '' - }, - { - id: 'logs', - amount: 1, - unit: '' - }, - { - id: 'members', - amount: 1, - unit: '' - }, - { - id: 'platforms', - amount: 1, - unit: '' - }, - { - id: 'webhooks', - amount: 1, - unit: '' - }, - { - id: 'databases', - amount: 1, - unit: '' - }, - { - id: 'connections', - amount: 1, - unit: '' - }, - { - id: 'messages', - amount: 1, - unit: '' - } - ] -}; - export enum BillingPlan { STARTER = 'tier-0', PRO = 'tier-1', diff --git a/src/lib/helpers/numbers.ts b/src/lib/helpers/numbers.ts index 796160cc2e..f9a0fdba63 100644 --- a/src/lib/helpers/numbers.ts +++ b/src/lib/helpers/numbers.ts @@ -20,3 +20,12 @@ export function formatNumberWithCommas(number: number): string { const formatter = new Intl.NumberFormat('en'); return formatter.format(number); } + +export function formatCurrency(number: number, locale = 'en-US', currency = 'USD'): string { + if (isNaN(number)) return String(number); + const formatter = new Intl.NumberFormat(locale, { + style: 'currency', + currency + }); + return formatter.format(number); +} diff --git a/src/routes/console/onboarding/+page.svelte b/src/routes/console/onboarding/+page.svelte index 8785fb04e8..39d318db97 100644 --- a/src/routes/console/onboarding/+page.svelte +++ b/src/routes/console/onboarding/+page.svelte @@ -18,6 +18,7 @@ import CreateOrganizationCloud from '../createOrganizationCloud.svelte'; import { tierToPlan, type Tier } from '$lib/stores/billing'; import { createOrganization } from '../wizard/cloudOrganization/store'; + import { formatCurrency } from '$lib/helpers/numbers'; let name: string; let id: string; @@ -25,8 +26,8 @@ let plan: Tier; const options = [ - { value: BillingPlan.STARTER, label: 'Starter - $0/month' }, - { value: BillingPlan.PRO, label: 'Pro - $15/month + add-ons' } + { value: BillingPlan.STARTER, label: `Starter - ${formatCurrency(0)}/month` }, + { value: BillingPlan.PRO, label: `Pro - ${formatCurrency(0)}/month + add-ons` } ]; onMount(() => { diff --git a/src/routes/console/organization-[organization]/billing/availableCredit.svelte b/src/routes/console/organization-[organization]/billing/availableCredit.svelte index dcaf3f3535..a2eed718bc 100644 --- a/src/routes/console/organization-[organization]/billing/availableCredit.svelte +++ b/src/routes/console/organization-[organization]/billing/availableCredit.svelte @@ -18,6 +18,7 @@ import AddCreditWizard from './addCreditWizard.svelte'; import { Button } from '$lib/elements/forms'; import AddCreditModal from './addCreditModal.svelte'; + import { formatCurrency } from '$lib/helpers/numbers'; import { BillingPlan } from '$lib/constants'; import ChangeOrganizationTierCloud from '$routes/console/changeOrganizationTierCloud.svelte'; import { trackEvent } from '$lib/actions/analytics'; @@ -68,7 +69,7 @@ } - + Available credit

Appwrite credit will automatically be applied to your next invoice.

@@ -88,7 +89,7 @@

Credit balance

- ${balance} + {formatCurrency(balance)}
{#if creditList?.total}
@@ -78,7 +78,7 @@ {extraMembers.value} {extraMembers.name}

-

${extraMembers.amount}

+

{formatCurrency(extraMembers.amount)}

@@ -98,7 +98,7 @@ {excess.name}

-

${excess.amount}

+

{formatCurrency(excess.amount)}

{/if} {#if ['users', 'executions'].includes(excess.name)} @@ -108,13 +108,15 @@ >{abbreviateNumber(excess.value)} {excess.name}

-

${excess.amount}

+

{formatCurrency(excess.amount)}

{/if} {/each}
  • Total to-date:

    -

    ${currentInvoice?.amount}

    +

    + {formatCurrency(currentInvoice?.amount ?? 0)} +

  • diff --git a/src/routes/console/organization-[organization]/createMember.svelte b/src/routes/console/organization-[organization]/createMember.svelte index 5bd7742ba2..338d08a3b5 100644 --- a/src/routes/console/organization-[organization]/createMember.svelte +++ b/src/routes/console/organization-[organization]/createMember.svelte @@ -11,6 +11,7 @@ import { Submit, trackEvent, trackError } from '$lib/actions/analytics'; import { isCloud } from '$lib/system'; import { plansInfo } from '$lib/stores/billing'; + import { formatCurrency } from '$lib/helpers/numbers'; export let showCreate = false; @@ -63,7 +64,7 @@ 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 - ${plan.addons.member.price} each per billing period. + {formatCurrency(plan.addons.member.price)} each per billing period. {/if} {/if} diff --git a/src/routes/console/wizard/cloudOrganization/confirmDetails.svelte b/src/routes/console/wizard/cloudOrganization/confirmDetails.svelte index 63194d59e9..a7c6209f6d 100644 --- a/src/routes/console/wizard/cloudOrganization/confirmDetails.svelte +++ b/src/routes/console/wizard/cloudOrganization/confirmDetails.svelte @@ -4,6 +4,7 @@ import { BillingPlan } from '$lib/constants'; import { Pill } from '$lib/elements'; import { toLocaleDate } from '$lib/helpers/date'; + import { formatCurrency } from '$lib/helpers/numbers'; import { WizardStep } from '$lib/layout'; import type { Coupon } from '$lib/sdk/billing'; import { plansInfo } from '$lib/stores/billing'; @@ -92,28 +93,30 @@ on:validation={(e) => ($createOrganization.couponCode = e.detail.code)} />

    {plan.name} plan

    -

    ${plan.price}

    +

    {formatCurrency(plan.price)}

    Additional members ({collaboratorsNumber})

    -

    ${collaboratorPrice * collaboratorsNumber}

    +

    {formatCurrency(collaboratorPrice * collaboratorsNumber)}

    {#if couponData?.status === 'active'}

    Credits applied ({couponData.credits})

    -

    -${couponData.credits}

    +

    -{formatCurrency(couponData.credits)}

    {/if}
    {/if} + {@const estimatedTotal = + couponData?.status === 'active' + ? totalExpences - couponData.credits >= 0 + ? totalExpences - couponData.credits + : 0 + : totalExpences}

    Estimated total

    - ${couponData?.status === 'active' - ? totalExpences - couponData.credits >= 0 - ? totalExpences - couponData.credits - : 0 - : totalExpences} + {formatCurrency(estimatedTotal)}

    diff --git a/src/routes/console/wizard/cloudOrganization/inviteMembers.svelte b/src/routes/console/wizard/cloudOrganization/inviteMembers.svelte index 3c84e8c819..e1359f39b7 100644 --- a/src/routes/console/wizard/cloudOrganization/inviteMembers.svelte +++ b/src/routes/console/wizard/cloudOrganization/inviteMembers.svelte @@ -11,6 +11,7 @@ TableHeader, TableRow } from '$lib/elements/table'; + import { formatCurrency } from '$lib/helpers/numbers'; import { WizardStep } from '$lib/layout'; import { plansInfo } from '$lib/stores/billing'; import { createOrganization } from './store'; @@ -47,8 +48,8 @@ added will receive an email invite to your organization on completion. {:else if $createOrganization.billingPlan === BillingPlan.PRO} You can add unlimited organization members on the {plan.name} plan for - ${plan.addons.member.price} each per month. Each member added will receive an - email invite to your organization on completion. + {formatCurrency(plan.addons.member.price)} each per month. Each member added will + receive an email invite to your organization on completion. {/if} @@ -81,7 +82,7 @@ {collaborator} {#if $createOrganization.billingPlan === BillingPlan.PRO} - 15$ + {formatCurrency(15)} {/if}
    @@ -100,8 +101,8 @@

    - {tierPro.name} - ${proPlan.price}/month per organization member + exta - usage + {tierPro.name} - {formatCurrency(proPlan?.price ?? 0)}/month per + organization member + exta usage

    {tierPro.description} @@ -122,7 +123,8 @@ class="u-flex u-flex-vertical u-gap-4 u-width-full-line" class:u-opacity-50={disabled}>

    - {tierScale.name} - ${scalePlan.price}/month + extra usage + {tierScale.name} - {formatCurrency(scalePlan?.price ?? 0)}/month + extra + usage

    {tierScale.description} diff --git a/src/routes/console/wizard/cloudOrganization/usageRates.svelte b/src/routes/console/wizard/cloudOrganization/usageRates.svelte index e90faac661..ac8fb21c58 100644 --- a/src/routes/console/wizard/cloudOrganization/usageRates.svelte +++ b/src/routes/console/wizard/cloudOrganization/usageRates.svelte @@ -13,7 +13,7 @@ import { organization } from '$lib/stores/organization'; import { createOrganization } from './store'; import { plansInfo, type Tier } from '$lib/stores/billing'; - import { abbreviateNumber } from '$lib/helpers/numbers'; + import { abbreviateNumber, formatCurrency } from '$lib/helpers/numbers'; import { BillingPlan } from '$lib/constants'; export let show = false; @@ -87,7 +87,7 @@ {#if !isFree} - ${plan.addons.member.price}/{usage?.unit} + {formatCurrency(plan.addons.member.price)}/{usage?.unit} {/if} @@ -100,7 +100,9 @@ {#if !isFree} - ${addon?.price}/{['MB', 'GB', 'TB'].includes(addon?.unit) + {formatCurrency(addon?.price)}/{['MB', 'GB', 'TB'].includes( + addon?.unit + ) ? addon?.value : abbreviateNumber(addon?.value, 0)}{usage?.unit} diff --git a/src/routes/console/wizard/cloudOrganizationChangeTier/choosePlan.svelte b/src/routes/console/wizard/cloudOrganizationChangeTier/choosePlan.svelte index 76ff8498ef..5a94e9ec8d 100644 --- a/src/routes/console/wizard/cloudOrganizationChangeTier/choosePlan.svelte +++ b/src/routes/console/wizard/cloudOrganizationChangeTier/choosePlan.svelte @@ -12,6 +12,7 @@ import { sizeToBytes } from '$lib/helpers/sizeConvertion'; import { Pill } from '$lib/elements'; import { BillingPlan } from '$lib/constants'; + import { formatCurrency } from '$lib/helpers/numbers'; let usage: OrganizationUsage = null; let members: Models.MembershipList = null; @@ -127,7 +128,7 @@ class="u-flex u-flex-vertical u-gap-4 u-width-full-line" class:u-opacity-50={disabled}>

    - {tierFree.name} - ${freePlan.price}/month + {tierFree.name} - {formatCurrency(freePlan.price)}/month

    {tierFree.description}

    @@ -150,8 +151,8 @@ class="u-flex u-flex-vertical u-gap-4 u-width-full-line" class:u-opacity-50={disabled}>

    - {tierPro.name} - ${proPlan.price}/month per organization member + extra - usage + {tierPro.name} - {formatCurrency(proPlan.price)}/month per organization + member + extra usage

    {tierPro.description} @@ -178,7 +179,7 @@ class="u-flex u-flex-vertical u-gap-4 u-width-full-line" class:u-opacity-50={disabled}>

    - {tierScale.name} - ${scalePlan.price}/month + extra usage + {tierScale.name} - {formatCurrency(scalePlan.price)}/month + extra usage

    {tierScale.description} diff --git a/src/routes/console/wizard/cloudOrganizationChangeTier/confirmDetails.svelte b/src/routes/console/wizard/cloudOrganizationChangeTier/confirmDetails.svelte index 7191ee3774..e62c6af3fb 100644 --- a/src/routes/console/wizard/cloudOrganizationChangeTier/confirmDetails.svelte +++ b/src/routes/console/wizard/cloudOrganizationChangeTier/confirmDetails.svelte @@ -4,6 +4,7 @@ import { BillingPlan } from '$lib/constants'; import { FormList, InputTextarea } from '$lib/elements/forms'; import { toLocaleDate } from '$lib/helpers/date'; + import { formatCurrency } from '$lib/helpers/numbers'; import { WizardStep } from '$lib/layout'; import type { Coupon } from '$lib/sdk/billing'; import { plansInfo } from '$lib/stores/billing'; @@ -104,28 +105,30 @@ on:validation={(e) => ($changeOrganizationTier.couponCode = e.detail.code)} />

    {plan.name} plan

    -

    ${plan.price}

    +

    {formatCurrency(plan.price)}

    Additional members ({collaboratorsNumber})

    -

    ${collaboratorPrice * collaboratorsNumber}

    +

    {formatCurrency(collaboratorPrice * collaboratorsNumber)}

    {#if couponData?.status === 'active'}

    Credits applied ({couponData.credits})

    -

    -${couponData.credits}

    +

    -{formatCurrency(couponData.credits)}

    {/if} {/if} + {@const estimatedTotal = + couponData?.status === 'active' + ? totalExpences - couponData.credits >= 0 + ? totalExpences - couponData.credits + : 0 + : totalExpences}

    Estimated total

    - ${couponData?.status === 'active' - ? totalExpences - couponData.credits >= 0 - ? totalExpences - couponData.credits - : 0 - : totalExpences} + {formatCurrency(estimatedTotal)}

    diff --git a/src/routes/console/wizard/cloudOrganizationChangeTier/inviteMembers.svelte b/src/routes/console/wizard/cloudOrganizationChangeTier/inviteMembers.svelte index 32e30c8b38..94d86e2671 100644 --- a/src/routes/console/wizard/cloudOrganizationChangeTier/inviteMembers.svelte +++ b/src/routes/console/wizard/cloudOrganizationChangeTier/inviteMembers.svelte @@ -18,6 +18,7 @@ import { user } from '$lib/stores/user'; import { organization } from '$lib/stores/organization'; import { BillingPlan } from '$lib/constants'; + import { formatCurrency } from '$lib/helpers/numbers'; const plan = $plansInfo.get($changeOrganizationTier.billingPlan); @@ -63,8 +64,8 @@ added will receive an email invite to your organization on completion. {:else if $changeOrganizationTier.billingPlan === BillingPlan.PRO} You can add unlimited organization members on the {plan.name} plan for - ${plan.addons.member.price} each per month. Each member added will receive an - email invite to your organization on completion. + {formatCurrency(plan.addons.member.price)} each per month. Each member added will + receive an email invite to your organization on completion. {/if} @@ -97,7 +98,7 @@ {collaborator} {#if $changeOrganizationTier.billingPlan === BillingPlan.PRO} - 15$ + {formatCurrency(15)} {/if}