feat: add comparison box

This commit is contained in:
Arman
2024-05-08 14:59:54 +02:00
parent c9f0ca8b8f
commit 07db6cd4e2
6 changed files with 185 additions and 121 deletions
+2 -2
View File
@@ -7,8 +7,8 @@
"name": "@appwrite/console",
"dependencies": {
"@appwrite.io/console": "^0.6.1",
"@appwrite.io/pink": "0.11.0",
"@appwrite.io/pink-icons": "0.11.0",
"@appwrite.io/pink": "0.16.0",
"@appwrite.io/pink-icons": "0.16.0",
"@popperjs/core": "^2.11.8",
"@sentry/svelte": "^7.66.0",
"@sentry/tracing": "^7.66.0",
@@ -0,0 +1,114 @@
<script lang="ts">
import { tooltip } from '$lib/actions/tooltip';
import { FormList, InputChoice, InputNumber } from '$lib/elements/forms';
import { toLocaleDate } from '$lib/helpers/date';
import { formatCurrency } from '$lib/helpers/numbers';
import type { Coupon } from '$lib/sdk/billing';
import { plansInfo, type Tier } from '$lib/stores/billing';
import { Box } from '..';
export let billingPlan: Tier;
export let collaborators: string[];
export let couponData: Partial<Coupon>;
export let billingBudget: number;
const today = new Date();
const billingPayDate = new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000);
let budgetEnabled = false;
$: currentPlan = $plansInfo.get(billingPlan);
$: extraSeatsCost = (collaborators?.length ?? 0) * (currentPlan?.addons?.member?.price ?? 0);
$: grossCost = currentPlan.price + extraSeatsCost;
$: estimatedTotal =
couponData?.status === 'active'
? grossCost - couponData.credits >= 0
? grossCost - couponData.credits
: 0
: grossCost;
</script>
<Box class="u-margin-block-start-32 u-flex u-flex-vertical u-gap-16" radius="small">
<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">
<p class="text">Additional seats ({collaborators?.length})</p>
<p class="text">
{formatCurrency(extraSeatsCost)}
</p>
</span>
{#if couponData?.status === 'active'}
<span class="u-flex u-main-space-between">
<div class="u-flex u-cross-center u-gap-4">
<p class="text">
<span class="icon-tag u-color-text-success" aria-hidden="true" />
{#if couponData.credits > 100}
{couponData.code.toUpperCase()}
{:else}
<span use:tooltip={{ content: couponData.code.toUpperCase() }}
>Credits applied</span>
{/if}
</p>
<button
type="button"
class="button is-text is-only-icon"
style="--button-size:1.5rem;"
aria-label="Close"
title="Close"
on:click={() =>
(couponData = {
code: null,
status: null,
credits: null
})}>
<span class="icon-x" aria-hidden="true" />
</button>
</div>
{#if couponData.credits > 100}
<p class="inline-tag" use:tooltip={{ content: formatCurrency(couponData.credits) }}>
Credits applied
</p>
{:else}
<span class="u-color-text-success">-{formatCurrency(couponData.credits)}</span>
{/if}
</span>
{/if}
<div class="u-sep-block-start" />
<span class="u-flex u-main-space-between">
<p class="text">Estimated total</p>
<p class="text">
{formatCurrency(estimatedTotal)}
</p>
</span>
{@const trialEndDate = new Date(
billingPayDate.getTime() + currentPlan.trialDays * 24 * 60 * 60 * 1000
)}
<p class="text u-margin-block-start-16">
Your payment method will be charged this amount plus usage fees every 30 days {!currentPlan.trialDays
? `starting ${toLocaleDate(billingPayDate.toString())}`
: ` after your trial period ends on ${toLocaleDate(trialEndDate.toString())}`}.
</p>
<FormList>
<InputChoice
type="switchbox"
id="budget"
label="Enable budget cap"
tooltip="If enabled, you will be notified when your spending reaches 75% of the set cap. Update cap alerts in your organization settings."
fullWidth
bind:value={budgetEnabled}>
{#if budgetEnabled}
<div class="u-margin-block-start-16">
<InputNumber
id="budget"
label="Budget cap (USD)"
placeholder="0"
min={0}
bind:value={billingBudget} />
</div>
{/if}
</InputChoice>
</FormList>
</Box>
+2
View File
@@ -2,3 +2,5 @@ export { default as PaymentBoxes } from './paymentBoxes.svelte';
export { default as CouponInput } from './couponInput.svelte';
export { default as SelectPaymentMethod } from './selectPaymentMethod.svelte';
export { default as UsageRates } from './usageRates.svelte';
export { default as EstimatedTotalBox } from './estimatedTotalBox.svelte';
export { default as PlanComparisonBox } from './planComparisonBox.svelte';
@@ -0,0 +1,50 @@
<script lang="ts">
import { formatNum } from '$lib/helpers/string';
import { plansInfo } from '$lib/stores/billing';
import { Box, SecondaryTabs, SecondaryTabsItem } from '..';
let selectedTab: 'tier-0' | 'tier-1' = 'tier-0';
$: plan = $plansInfo.get(selectedTab);
</script>
<Box>
<SecondaryTabs stretch>
<SecondaryTabsItem
disabled={selectedTab === 'tier-0'}
on:click={() => (selectedTab = 'tier-0')}>
Starter
</SecondaryTabsItem>
<SecondaryTabsItem
disabled={selectedTab === 'tier-1'}
on:click={() => (selectedTab = 'tier-1')}>
Pro
</SecondaryTabsItem>
</SecondaryTabs>
<div class="u-margin-block-start-24">
{#if selectedTab === 'tier-0'}
<h3 class="u-bold body-text-1">{plan.name} plan</h3>
<ul class="list u-margin-block-start-8">
<li class="list-item">
Limited to {plan.databases} Database, {plan.buckets} Buckets, {plan.functions} Functions
per project
</li>
<li class="list-item">Limited to 1 organization member</li>
<li class="list-item">{plan.bandwidth}GB bandwidth</li>
<li class="list-item">{plan.storage}GB storage</li>
<li class="list-item">{formatNum(plan.executions)} executions</li>
</ul>
{:else if selectedTab === 'tier-1'}
<h3 class="u-bold body-text-1">{plan.name} plan</h3>
<ul class="list u-margin-block-start-8">
<li class="list-item">Everything in the Starter plan, plus:</li>
<li class="list-item">Unlimited databases, buckets, functions</li>
<li class="list-item">{plan.bandwidth}GB bandwidth</li>
<li class="list-item">{plan.storage}GB storage</li>
<li class="list-item">{formatNum(plan.executions)} executions</li>
<li class="list-item">Email support</li>
</ul>
{/if}
</div>
</Box>
+2 -1
View File
@@ -1,9 +1,10 @@
<script lang="ts">
export let large = false;
export let stretch = false;
let classes: string = undefined;
export { classes as class };
</script>
<ul class="secondary-tabs {classes}" class:is-large={large}>
<ul class="secondary-tabs {classes}" class:is-large={large} class:is-stretch={stretch}>
<slot />
</ul>
@@ -3,22 +3,15 @@
import { base } from '$app/paths';
import { page } from '$app/stores';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { tooltip } from '$lib/actions/tooltip';
import { Box, LabelCard } from '$lib/components';
import { SelectPaymentMethod } from '$lib/components/billing';
import { LabelCard } from '$lib/components';
import {
EstimatedTotalBox,
PlanComparisonBox,
SelectPaymentMethod
} from '$lib/components/billing';
import ValidateCreditModal from '$lib/components/billing/validateCreditModal.svelte';
import { BillingPlan, Dependencies } from '$lib/constants';
import {
Button,
Form,
FormList,
InputChoice,
InputNumber,
InputTags,
InputText,
Label
} from '$lib/elements/forms';
import { toLocaleDate } from '$lib/helpers/date';
import { Button, Form, FormList, InputTags, InputText, Label } from '$lib/elements/forms';
import { formatCurrency } from '$lib/helpers/numbers';
import {
WizardSecondaryContainer,
@@ -39,9 +32,6 @@
(org) => (org as Organization)?.billingPlan === BillingPlan.STARTER
);
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$/i;
const today = new Date();
const billingPayDate = new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000);
let previousPage: string = `${base}/console`;
afterNavigate(({ from }) => {
@@ -63,7 +53,7 @@
credits: null
};
let taxId: string;
let budgetEnabled = false;
let billingBudget: number;
let showCreditModal = false;
@@ -190,15 +180,6 @@
$: if (billingPlan === BillingPlan.PRO) {
loadPaymentMethods();
}
$: currentPlan = $plansInfo.get(billingPlan);
$: extraSeatsCost = (collaborators?.length ?? 0) * (currentPlan?.addons?.member?.price ?? 0);
$: grossCost = currentPlan.price + extraSeatsCost;
$: estimatedTotal =
couponData?.status === 'active'
? grossCost - couponData.credits >= 0
? grossCost - couponData.credits
: 0
: grossCost;
</script>
<svelte:head>
@@ -293,97 +274,13 @@
</Form>
<svelte:fragment slot="aside">
{#if billingPlan !== BillingPlan.STARTER}
<Box class="u-margin-block-start-32 u-flex u-flex-vertical u-gap-16" radius="small">
<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">
<p class="text">Additional seats ({collaborators?.length})</p>
<p class="text">
{formatCurrency(extraSeatsCost)}
</p>
</span>
{#if couponData?.status === 'active'}
<span class="u-flex u-main-space-between">
<div class="u-flex u-cross-center u-gap-4">
<p class="text">
<span
class="icon-tag u-color-text-success"
aria-hidden="true" />
{#if couponData.credits > 100}
{couponData.code.toUpperCase()}
{:else}
<span
use:tooltip={{ content: couponData.code.toUpperCase() }}
>Credits applied</span>
{/if}
</p>
<button
type="button"
class="button is-text is-only-icon"
style="--button-size:1.5rem;"
aria-label="Close"
title="Close"
on:click={() =>
(couponData = {
code: null,
status: null,
credits: null
})}>
<span class="icon-x" aria-hidden="true" />
</button>
</div>
{#if couponData.credits > 100}
<p
class="inline-tag"
use:tooltip={{ content: formatCurrency(couponData.credits) }}>
Credits applied
</p>
{:else}
<span class="u-color-text-success"
>-{formatCurrency(couponData.credits)}</span>
{/if}
</span>
{/if}
<div class="u-sep-block-start" />
<span class="u-flex u-main-space-between">
<p class="text">Estimated total</p>
<p class="text">
{formatCurrency(estimatedTotal)}
</p>
</span>
{@const trialEndDate = new Date(
billingPayDate.getTime() + currentPlan.trialDays * 24 * 60 * 60 * 1000
)}
<p class="text u-margin-block-start-16">
Your payment method will be charged this amount plus usage fees every 30
days {!currentPlan.trialDays
? `starting ${toLocaleDate(billingPayDate.toString())}`
: ` after your trial period ends on ${toLocaleDate(trialEndDate.toString())}`}.
</p>
<FormList>
<InputChoice
type="switchbox"
id="budget"
label="Enable budget cap"
tooltip="If enabled, you will be notified when your spending reaches 75% of the set cap. Update cap alerts in your organization settings."
fullWidth
bind:value={budgetEnabled}>
{#if budgetEnabled}
<div class="u-margin-block-start-16">
<InputNumber
id="budget"
label="Budget cap (USD)"
placeholder="0"
min={0}
bind:value={billingBudget} />
</div>
{/if}
</InputChoice>
</FormList>
</Box>
<EstimatedTotalBox
{billingPlan}
{collaborators}
bind:couponData
bind:billingBudget />
{:else}
<PlanComparisonBox />
{/if}
</svelte:fragment>
</WizardSecondaryContent>