Skip to content

Commit

Permalink
Merge pull request #1565 from ItzNotABug/poc-invoice-cycle-ref
Browse files Browse the repository at this point in the history
Updated pricing estimates
  • Loading branch information
lohanidamodar authored Jan 13, 2025
2 parents 81e5d0d + 34f0727 commit 3ed6edf
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 50 deletions.
103 changes: 76 additions & 27 deletions src/lib/components/billing/estimatedTotalBox.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
import type { Coupon } from '$lib/sdk/billing';
import { plansInfo, type Tier } from '$lib/stores/billing';
import { CreditsApplied } from '.';
import { BillingPlan } from '$lib/constants';
import { tooltip } from '$lib/actions/tooltip';
// undefined as we only need this on `change-plan`
export let currentTier: Tier | undefined = undefined;
export let billingPlan: Tier;
export let collaborators: string[];
export let couponData: Partial<Coupon>;
Expand All @@ -14,21 +18,29 @@
export let isDowngrade = false;
const today = new Date();
const isScaleDowngrade = isDowngrade && billingPlan === BillingPlan.PRO;
const isScaleUpgrade = !isDowngrade && billingPlan === BillingPlan.SCALE;
const billingPayDate = new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000);
let budgetEnabled = false;
$: currentPlan = $plansInfo.get(billingPlan);
$: extraSeatsCost = 0; // 0 untile trial period later replace (collaborators?.length ?? 0) * (currentPlan?.addons?.member?.price ?? 0);
$: grossCost = currentPlan.price + extraSeatsCost;
$: selectedPlan = $plansInfo.get(billingPlan);
$: currentOrgPlan = $plansInfo.get(currentTier);
$: unUsedBalances = isScaleUpgrade
? currentOrgPlan.price +
(collaborators?.length ?? 0) * (currentOrgPlan?.addons?.member?.price ?? 0)
: isScaleDowngrade
? currentOrgPlan.price
: 0;
$: extraSeatsCost = (collaborators?.length ?? 0) * (selectedPlan?.addons?.member?.price ?? 0);
$: grossCost = isScaleUpgrade
? selectedPlan.price + extraSeatsCost - unUsedBalances
: selectedPlan.price + extraSeatsCost;
$: estimatedTotal =
couponData?.status === 'active'
? grossCost - couponData.credits >= 0
? grossCost - couponData.credits
: 0
: grossCost;
couponData?.status === 'active' ? Math.max(0, grossCost - couponData.credits) : grossCost;
$: trialEndDate = new Date(
billingPayDate.getTime() + currentPlan.trialDays * 24 * 60 * 60 * 1000
billingPayDate.getTime() + selectedPlan.trialDays * 24 * 60 * 60 * 1000
);
</script>

Expand All @@ -37,41 +49,78 @@
style:--p-card-padding="1.5rem"
style:--p-card-border-radius="var(--border-radius-small)">
<slot />
<span class="u-flex u-main-space-between">
<p class="text">{currentPlan.name} plan</p>
<p class="text">{formatCurrency(currentPlan.price)}</p>
</span>
<span class="u-flex u-main-space-between">
<div class="u-flex u-main-space-between">
<p class="text">{selectedPlan.name} plan</p>
<p class="text">{formatCurrency(selectedPlan.price)}</p>
</div>

<div class="u-flex u-main-space-between">
<p class="text" class:u-bold={isDowngrade}>Additional seats ({collaborators?.length})</p>
<p class="text" class:u-bold={isDowngrade}>
{formatCurrency(extraSeatsCost)}
{formatCurrency(
isScaleDowngrade
? (collaborators?.length ?? 0) * (selectedPlan?.addons?.member?.price ?? 0)
: extraSeatsCost
)}
</p>
</span>
</div>

{#if isScaleUpgrade}
{@const currentPlanName = currentOrgPlan.name}
<div class="u-flex u-main-space-between">
<div class="text">
<span>Unused {currentPlanName} plan balance</span>
<span
use:tooltip={{
placement: 'bottom',
content: `This discount reflects the unused portion of your ${currentPlanName} plan and add-ons. Future credits for extra seats and features will apply automatically.`
}}
class="icon-info">
</span>
</div>
<p class="text">-{formatCurrency(unUsedBalances)}</p>
</div>
{/if}

{#if couponData?.status === 'active'}
<CreditsApplied bind:couponData {fixedCoupon} />
{/if}
<div class="u-sep-block-start" />
<span class="u-flex u-main-space-between">
<div class="u-flex u-main-space-between">
<p class="text">
Upcoming charge<br /><span class="u-color-text-gray"
>Due on {!currentPlan.trialDays
>Due on {!selectedPlan.trialDays
? toLocaleDate(billingPayDate.toString())
: toLocaleDate(trialEndDate.toString())}</span>
</p>
<p class="text">
{formatCurrency(estimatedTotal)}
</p>
</span>
</div>

<p class="text u-margin-block-start-16">
You'll pay <span class="u-bold">{formatCurrency(estimatedTotal)}</span> now, with our first
billing cycle starting on
<span class="u-bold"
>{!currentPlan.trialDays
? toLocaleDate(billingPayDate.toString())
: toLocaleDate(trialEndDate.toString())}</span
>. Once your credits run out, you'll be charged
<span class="u-bold">{formatCurrency(currentPlan.price)}</span> plus usage fees every 30 days.
{#if isScaleDowngrade}
You'll continue using the {currentOrgPlan.name} plan until your current cycle ends on
<span class="u-bold"
>{!selectedPlan.trialDays
? toLocaleDate(billingPayDate.toString())
: toLocaleDate(trialEndDate.toString())}</span
>. Starting the next cycle, your plan will switch to {selectedPlan.name}, and you'll be
charged
<span class="u-bold">{formatCurrency(grossCost)}</span>
every 30 days.
{:else}
You'll pay
<span class="u-bold">{formatCurrency(estimatedTotal)}</span>
now, with our first billing cycle starting on
<span class="u-bold"
>{!selectedPlan.trialDays
? toLocaleDate(billingPayDate.toString())
: toLocaleDate(trialEndDate.toString())}</span
>. Once your credits run out, you'll be charged
<span class="u-bold">{formatCurrency(selectedPlan.price)}</span> plus usage fees every 30
days.
{/if}
</p>

<FormList class="u-margin-block-start-24">
Expand Down
6 changes: 1 addition & 5 deletions src/lib/components/billing/planSelection.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,7 @@
</LabelCard>
</li>
<li>
<LabelCard
name="plan"
bind:group={billingPlan}
value={BillingPlan.SCALE}
padding={1.5}>
<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">
Expand Down
7 changes: 2 additions & 5 deletions src/lib/sdk/billing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,14 +342,11 @@ export class Billing {
);
}

async validateOrganization(
organizationId: string,
invites: string[],
): Promise<Organization> {
async validateOrganization(organizationId: string, invites: string[]): Promise<Organization> {
const path = `/organizations/${organizationId}/validate`;
const params = {
organizationId,
invites,
invites
};
const uri = new URL(this.client.config.endpoint + path);
return await this.client.call(
Expand Down
4 changes: 2 additions & 2 deletions src/lib/stores/stripe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ export async function confirmPayment(
clientSecret: clientSecret,
confirmParams: {
return_url: url,
payment_method: paymentMethod.providerMethodId,
},
payment_method: paymentMethod.providerMethodId
}
});
if (error) {
if (returnError) {
Expand Down
10 changes: 4 additions & 6 deletions src/routes/(console)/account/organizations/+page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@ export const load: PageLoad = async ({ url, route }) => {
const limit = getLimit(url, route, CARD_LIMIT);
const offset = pageToOffset(page, limit);

const queries = [
Query.offset(offset),
Query.limit(limit),
Query.orderDesc('')
];
const queries = [Query.offset(offset), Query.limit(limit), Query.orderDesc('')];

const organizations = isCloud ? await sdk.forConsole.billing.listOrganization(queries) : sdk.forConsole.teams.list(queries);
const organizations = isCloud
? await sdk.forConsole.billing.listOrganization(queries)
: sdk.forConsole.teams.list(queries);

return {
offset,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
InputTextarea,
Label
} from '$lib/elements/forms';
import { formatCurrency } from '$lib/helpers/numbers.js';
import { formatCurrency } from '$lib/helpers/numbers';
import {
WizardSecondaryContainer,
WizardSecondaryContent,
Expand All @@ -40,7 +40,7 @@
type Organization
} from '$lib/stores/organization';
import { sdk } from '$lib/stores/sdk';
import { confirmPayment } from '$lib/stores/stripe.js';
import { confirmPayment } from '$lib/stores/stripe';
import { user } from '$lib/stores/user';
import { VARS } from '$lib/system';
import { onMount } from 'svelte';
Expand Down Expand Up @@ -212,7 +212,7 @@
async function upgrade() {
try {
//Add collaborators
var newCollaborators = [];
let newCollaborators = [];
if (collaborators?.length) {
newCollaborators = collaborators.filter(
(collaborator) =>
Expand Down Expand Up @@ -371,11 +371,12 @@
<svelte:fragment slot="aside">
{#if billingPlan !== BillingPlan.FREE && $organization.billingPlan !== billingPlan && $organization.billingPlan !== BillingPlan.CUSTOM}
<EstimatedTotalBox
{isDowngrade}
{billingPlan}
{collaborators}
bind:couponData
bind:billingBudget
{isDowngrade} />
currentTier={$organization.billingPlan} />
{:else if $organization.billingPlan !== BillingPlan.CUSTOM}
<PlanComparisonBox downgrade={isDowngrade} />
{/if}
Expand Down
4 changes: 3 additions & 1 deletion src/routes/+layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ export const load: LayoutLoad = async ({ depends, url, route }) => {
if (account) {
return {
account,
organizations: isCloud ? await sdk.forConsole.billing.listOrganization() : await sdk.forConsole.teams.list()
organizations: isCloud
? await sdk.forConsole.billing.listOrganization()
: await sdk.forConsole.teams.list()
};
}

Expand Down

0 comments on commit 3ed6edf

Please sign in to comment.