mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
update: simplify billing context & checks.
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { HeaderAlert } from '$lib/layout';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { upgradeURL } from '$lib/stores/billing';
|
||||
import { isFreeTier, upgradeURL } from '$lib/stores/billing';
|
||||
import { hideNotification } from '$lib/helpers/notifications';
|
||||
import { backupsBannerId, showPolicyAlert } from '$lib/stores/database';
|
||||
|
||||
@@ -16,7 +15,7 @@
|
||||
</script>
|
||||
|
||||
{#if $showPolicyAlert && isCloud && $organization?.$id && $page.url.pathname.match(/\/databases\/database-[^/]+$/)}
|
||||
{@const isFreePlan = $organization?.billingPlan === BillingPlan.FREE}
|
||||
{@const isFreePlan = isFreeTier($organization?.billingPlan)}
|
||||
|
||||
{@const subtitle = isFreePlan
|
||||
? 'Upgrade your plan to ensure your data stays safe and backed up'
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { isCloud, isSelfHosted } from '$lib/system';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { BillingPlan, Dependencies } from '$lib/constants';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import type { BackupArchive, BackupRestoration } from '$lib/sdk/backups';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
@@ -12,6 +12,7 @@
|
||||
import { base } from '$app/paths';
|
||||
import { getProjectId } from '$lib/helpers/project';
|
||||
import { toLocaleDate } from '$lib/helpers/date';
|
||||
import { isFreeTier } from '$lib/stores/billing';
|
||||
|
||||
const backupRestoreItems: {
|
||||
archives: Map<string, BackupArchive>;
|
||||
@@ -120,7 +121,7 @@
|
||||
|
||||
onMount(() => {
|
||||
// fast path: don't subscribe if org is on a free plan or is self-hosted.
|
||||
if (isSelfHosted || (isCloud && $organization.billingPlan === BillingPlan.FREE)) return;
|
||||
if (isSelfHosted || (isCloud && isFreeTier($organization.billingPlan))) return;
|
||||
|
||||
return sdk.forConsole.client.subscribe('console', (response) => {
|
||||
if (!response.channels.includes(`projects.${getProjectId()}`)) return;
|
||||
|
||||
@@ -5,11 +5,17 @@
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { HeaderAlert } from '$lib/layout';
|
||||
import { hideBillingHeaderRoutes, readOnly, tierToPlan, upgradeURL } from '$lib/stores/billing';
|
||||
import {
|
||||
hideBillingHeaderRoutes,
|
||||
isFreeTier,
|
||||
readOnly,
|
||||
tierToPlan,
|
||||
upgradeURL
|
||||
} from '$lib/stores/billing';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
</script>
|
||||
|
||||
{#if $organization?.$id && $organization?.billingPlan === BillingPlan.FREE && $readOnly && !hideBillingHeaderRoutes.includes($page.url.pathname)}
|
||||
{#if $organization?.$id && isFreeTier($organization?.billingPlan) && $readOnly && !hideBillingHeaderRoutes.includes($page.url.pathname)}
|
||||
<HeaderAlert
|
||||
type="error"
|
||||
title={`${$organization.name} usage has reached the ${tierToPlan($organization.billingPlan).name} plan limit`}>
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { BillingPlan, NEW_DEV_PRO_UPGRADE_COUPON } from '$lib/constants';
|
||||
import { NEW_DEV_PRO_UPGRADE_COUPON } from '$lib/constants';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { activeHeaderAlert } from '$routes/(console)/store';
|
||||
import GradientBanner from '../gradientBanner.svelte';
|
||||
import { isFreeTier } from '$lib/stores/billing';
|
||||
|
||||
let show = true;
|
||||
|
||||
@@ -21,7 +22,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if show && $organization?.$id && $organization?.billingPlan === BillingPlan.FREE && !$page.url.pathname.includes('/console/account')}
|
||||
{#if show && $organization?.$id && isFreeTier($organization?.billingPlan) && !$page.url.pathname.includes('/console/account')}
|
||||
<GradientBanner on:close={handleClose}>
|
||||
<div class="u-flex u-gap-24 u-main-center u-cross-center u-flex-vertical-mobile">
|
||||
<span class="body-text-1">Get $50 Cloud credits for Appwrite Pro.</span>
|
||||
|
||||
@@ -1,38 +1,32 @@
|
||||
<script lang="ts">
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { formatNum } from '$lib/helpers/string';
|
||||
import { plansInfo, tierFree, tierPro, tierScale, type Tier } from '$lib/stores/billing';
|
||||
import { isFreeTier, plansInfo, type Tier } from '$lib/stores/billing';
|
||||
import { Card, SecondaryTabs, SecondaryTabsItem } from '..';
|
||||
|
||||
let selectedTab: Tier = BillingPlan.FREE;
|
||||
export let downgrade = false;
|
||||
|
||||
// get the first available tier as default.
|
||||
let selectedTab: Tier = $plansInfo.keys().next().value;
|
||||
|
||||
$: plan = $plansInfo.get(selectedTab);
|
||||
</script>
|
||||
|
||||
<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>
|
||||
{#each $plansInfo.entries() as [tier, plan] (tier)}
|
||||
<SecondaryTabsItem
|
||||
disabled={selectedTab === tier}
|
||||
on:click={() => (selectedTab = tier)}>
|
||||
{plan.name}
|
||||
</SecondaryTabsItem>
|
||||
{/each}
|
||||
</SecondaryTabs>
|
||||
</div>
|
||||
|
||||
<div class="u-margin-block-start-24">
|
||||
{#if selectedTab === BillingPlan.FREE}
|
||||
{#if isFreeTier(selectedTab)}
|
||||
<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">
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<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 { isFreeTier, plansInfo, type Tier } from '$lib/stores/billing';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { LabelCard } from '..';
|
||||
|
||||
@@ -11,97 +10,40 @@
|
||||
export let selfService = true;
|
||||
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 || !selfService}
|
||||
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"
|
||||
disabled={!selfService}
|
||||
bind:group={billingPlan}
|
||||
value={BillingPlan.PRO}
|
||||
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">
|
||||
{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}
|
||||
disabled={!selfService}>
|
||||
<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">
|
||||
{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>
|
||||
{#each $plansInfo.entries() as [tier, plan] (tier)}
|
||||
<li>
|
||||
<LabelCard
|
||||
name="plan"
|
||||
bind:group={billingPlan}
|
||||
disabled={anyOrgFree || !selfService}
|
||||
value={tier}
|
||||
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">
|
||||
{plan.name}
|
||||
{#if isFreeTier($organization?.billingPlan) && !isNewOrg}
|
||||
<span class="inline-tag">Current plan</span>
|
||||
{/if}
|
||||
</h4>
|
||||
<p class="u-color-text-offline u-small">
|
||||
{plan.desc}
|
||||
</p>
|
||||
<p>
|
||||
{formatCurrency(plan?.price ?? 0)}
|
||||
</p>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</LabelCard>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { formatCurrency } from '$lib/helpers/numbers';
|
||||
import { plansInfo } from '$lib/stores/billing';
|
||||
import { isFreeTier, plansInfo } from '$lib/stores/billing';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { LabelCard } from '..';
|
||||
|
||||
@@ -14,15 +13,15 @@
|
||||
|
||||
{#if billingPlan}
|
||||
<ul class="u-flex u-flex-vertical u-gap-16 u-margin-block-start-8 {classes}">
|
||||
{#each $plansInfo.values() as plan}
|
||||
{#each $plansInfo.entries() as [tier, plan]}
|
||||
<li>
|
||||
<LabelCard
|
||||
name="plan"
|
||||
bind:group={billingPlan}
|
||||
disabled={(plan.$id === BillingPlan.FREE && anyOrgFree) || !plan.selfService}
|
||||
value={plan.$id}
|
||||
tooltipShow={plan.$id === BillingPlan.FREE && anyOrgFree}
|
||||
tooltipText={plan.$id === BillingPlan.FREE
|
||||
disabled={(isFreeTier(tier) && anyOrgFree) || !plan.selfService}
|
||||
value={tier}
|
||||
tooltipShow={isFreeTier(tier) && anyOrgFree}
|
||||
tooltipText={isFreeTier(tier)
|
||||
? 'You are limited to 1 Free organization per account.'
|
||||
: ''}
|
||||
padding={1.5}>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
} from '$lib/elements/table';
|
||||
import { toLocaleDate } from '$lib/helpers/date';
|
||||
import { organization, type Organization } from '$lib/stores/organization';
|
||||
import { plansInfo } from '$lib/stores/billing';
|
||||
import { isFreeTier, plansInfo } from '$lib/stores/billing';
|
||||
import { abbreviateNumber, formatCurrency } from '$lib/helpers/numbers';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
}
|
||||
];
|
||||
|
||||
$: isFree = org.billingPlan === BillingPlan.FREE;
|
||||
$: isFree = isFreeTier(org.billingPlan);
|
||||
</script>
|
||||
|
||||
<Modal bind:show size="big" headerDivider={false} title="Usage rates">
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { upgradeURL } from '$lib/stores/billing';
|
||||
import { isFreeTier, upgradeURL } from '$lib/stores/billing';
|
||||
import { addBottomModalAlerts } from '$routes/(console)/bottomAlerts';
|
||||
import { project } from '$routes/(console)/project-[project]/store';
|
||||
import { page } from '$app/stores';
|
||||
@@ -74,11 +74,9 @@
|
||||
case 'free':
|
||||
return false;
|
||||
case 'pro':
|
||||
return organizationPlan === BillingPlan.FREE;
|
||||
return isFreeTier(organizationPlan);
|
||||
case 'scale':
|
||||
return (
|
||||
organizationPlan === BillingPlan.FREE || organizationPlan === BillingPlan.PRO
|
||||
);
|
||||
return isFreeTier(organizationPlan) || organizationPlan === BillingPlan.PRO;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<script>
|
||||
import { getNextTier, tierToPlan } from '$lib/stores/billing';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import Card from './card.svelte';
|
||||
|
||||
export let source = 'empty_state_card';
|
||||
@@ -22,7 +20,7 @@
|
||||
<div class="u-stretch u-flex-vertical u-main-center">
|
||||
<h3 class="body-text-2 u-bold"><slot name="title" /></h3>
|
||||
<p class="u-margin-block-start-8">
|
||||
<slot nextTier={tierToPlan(getNextTier($organization.billingPlan)).name} />
|
||||
<slot />
|
||||
</p>
|
||||
<slot name="cta" {source} />
|
||||
</div>
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
<script>
|
||||
import Base from './base.svelte';
|
||||
import { upgradeURL } from '$lib/stores/billing';
|
||||
import { isFreeTier, upgradeURL } from '$lib/stores/billing';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import Button from '$lib/elements/forms/button.svelte';
|
||||
</script>
|
||||
|
||||
<Base>
|
||||
{#if isCloud}
|
||||
{#if $organization?.billingPlan !== BillingPlan.FREE}
|
||||
{#if !isFreeTier($organization?.billingPlan)}
|
||||
<div class="u-flex-vertical u-gap-8">
|
||||
<p>
|
||||
<span class="u-bold">Roles</span>
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier($organization?.billingPlan)}
|
||||
<span class="inline-tag u-normal u-x-small">Pro plan</span>
|
||||
{/if}
|
||||
</p>
|
||||
@@ -27,7 +26,7 @@
|
||||
<div class="u-flex-vertical u-gap-8">
|
||||
<p>
|
||||
<span class="u-bold">Roles</span>
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier($organization?.billingPlan)}
|
||||
<span class="inline-tag u-normal u-x-small">Pro plan</span>
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
@@ -18,11 +18,10 @@
|
||||
import { Container, ContainerHeader } from '$lib/layout';
|
||||
import { hoursToDays, toLocaleDateTime } from '$lib/helpers/date';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { tierToPlan, type PlanServices } from '$lib/stores/billing';
|
||||
import { tierToPlan, type PlanServices, isFreeTier } from '$lib/stores/billing';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
|
||||
export let logs: Models.LogList;
|
||||
export let offset = 0;
|
||||
@@ -52,7 +51,7 @@
|
||||
Logs are retained in rolling {hoursToDays(limit)} intervals with the
|
||||
{tierToPlan($organization.billingPlan).name}
|
||||
plan.
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier($organization?.billingPlan)}
|
||||
<Button link on:click={upgradeMethod}>Upgrade</Button> to increase your log
|
||||
retention for a longer period.
|
||||
{/if}
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { tierToPlan } from '$lib/stores/billing';
|
||||
import { isFreeTier, tierToPlan } from '$lib/stores/billing';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
|
||||
export let title: string;
|
||||
export let tooltipContent =
|
||||
$organization?.billingPlan === BillingPlan.FREE
|
||||
? `Upgrade to add more ${title.toLocaleLowerCase()}`
|
||||
: `You've reached the ${title.toLocaleLowerCase()} limit for the ${
|
||||
tierToPlan($organization?.billingPlan)?.name
|
||||
} plan`;
|
||||
export let tooltipContent = isFreeTier($organization?.billingPlan)
|
||||
? `Upgrade to add more ${title.toLocaleLowerCase()}`
|
||||
: `You've reached the ${title.toLocaleLowerCase()} limit for the ${
|
||||
tierToPlan($organization?.billingPlan)?.name
|
||||
} plan`;
|
||||
export let disabled: boolean;
|
||||
export let buttonText: string;
|
||||
export let buttonMethod: () => void | Promise<void>;
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
showUsageRatesModal,
|
||||
tierToPlan,
|
||||
upgradeURL,
|
||||
type PlanServices
|
||||
type PlanServices,
|
||||
isFreeTier
|
||||
} from '$lib/stores/billing';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { GRACE_PERIOD_OVERRIDE, isCloud } from '$lib/system';
|
||||
@@ -64,7 +65,7 @@
|
||||
|
||||
$: tier = tierToPlan($organization?.billingPlan)?.name;
|
||||
$: hasProjectLimitation =
|
||||
checkForProjectLimitation(serviceId) && $organization?.billingPlan === BillingPlan.FREE;
|
||||
checkForProjectLimitation(serviceId) && isFreeTier($organization?.billingPlan);
|
||||
$: hasUsageFees = hasProjectLimitation
|
||||
? checkForUsageFees($organization?.billingPlan, serviceId)
|
||||
: false;
|
||||
@@ -95,7 +96,7 @@
|
||||
|
||||
{#if services.length}
|
||||
<slot name="alert" {limit} {tier} {title} {upgradeMethod} {hasUsageFees} {services}>
|
||||
{#if $organization?.billingPlan !== BillingPlan.FREE && hasUsageFees}
|
||||
{#if !isFreeTier($organization?.billingPlan) && hasUsageFees}
|
||||
<Alert type="info" isStandalone>
|
||||
<span class="text">
|
||||
You've reached the {services} limit for the {tier} plan.
|
||||
@@ -142,7 +143,7 @@
|
||||
<p class="text">
|
||||
You are limited to {limit}
|
||||
{title.toLocaleLowerCase()} per project on the {tier} plan.
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}<Button
|
||||
{#if isFreeTier($organization?.billingPlan)}<Button
|
||||
link
|
||||
href={$upgradeURL}
|
||||
on:click={() =>
|
||||
@@ -165,7 +166,7 @@
|
||||
<p class="text">
|
||||
You are limited to {limit}
|
||||
{title.toLocaleLowerCase()} per organization on the {tier} plan.
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier($organization?.billingPlan)}
|
||||
<Button link href={$upgradeURL}>Upgrade</Button>
|
||||
for additional {title.toLocaleLowerCase()}.
|
||||
{/if}
|
||||
|
||||
@@ -20,9 +20,8 @@
|
||||
import { slide } from 'svelte/transition';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { Feedback } from '$lib/components/feedback';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { logout } from '$lib/helpers/logout';
|
||||
import { upgradeURL } from '$lib/stores/billing';
|
||||
import { isFreeTier, upgradeURL } from '$lib/stores/billing';
|
||||
|
||||
let showDropdown = false;
|
||||
let showSupport = false;
|
||||
@@ -93,7 +92,7 @@
|
||||
|
||||
<div class="main-header-end">
|
||||
<nav class="u-flex is-only-desktop u-cross-center">
|
||||
{#if isCloud && $organization?.billingPlan === BillingPlan.FREE && !$page.url.pathname.startsWith('/console/account')}
|
||||
{#if isCloud && isFreeTier($organization?.billingPlan) && !$page.url.pathname.startsWith('/console/account')}
|
||||
<Button
|
||||
disabled={$organization?.markedForDeletion}
|
||||
href={$upgradeURL}
|
||||
|
||||
@@ -14,10 +14,9 @@
|
||||
import { beforeNavigate } from '$app/navigation';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { getServiceLimit, tierToPlan, upgradeURL } from '$lib/stores/billing';
|
||||
import { getServiceLimit, isFreeTier, tierToPlan, upgradeURL } from '$lib/stores/billing';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
|
||||
let selectedRequest = 'parameters';
|
||||
@@ -345,7 +344,7 @@
|
||||
<Alert>
|
||||
Logs are retained in rolling {hoursToDays(limit)} intervals
|
||||
with the {tier} plan.
|
||||
{#if $organization.billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier($organization?.billingPlan)}
|
||||
<Button link href={$upgradeURL}>Upgrade</Button> to increase
|
||||
your log retention for a longer period.
|
||||
{/if}
|
||||
|
||||
+10
-75
@@ -68,45 +68,16 @@ export function getRoleLabel(role: string) {
|
||||
return roles.find((r) => r.value === role)?.label ?? role;
|
||||
}
|
||||
|
||||
export function tierToPlan(tier: Tier) {
|
||||
switch (tier) {
|
||||
case BillingPlan.FREE:
|
||||
return tierFree;
|
||||
case BillingPlan.PRO:
|
||||
return tierPro;
|
||||
case BillingPlan.SCALE:
|
||||
return tierScale;
|
||||
case BillingPlan.GITHUB_EDUCATION:
|
||||
return tierGitHubEducation;
|
||||
case BillingPlan.CUSTOM:
|
||||
return tierCustom;
|
||||
case BillingPlan.ENTERPRISE:
|
||||
return tierEnterprise;
|
||||
default:
|
||||
return tierFree;
|
||||
}
|
||||
export function tierToPlan(tier: Tier): Plan {
|
||||
return get(plansInfo).get(tier);
|
||||
}
|
||||
|
||||
export function getNextTier(tier: Tier) {
|
||||
switch (tier) {
|
||||
case BillingPlan.FREE:
|
||||
return BillingPlan.PRO;
|
||||
case BillingPlan.PRO:
|
||||
return BillingPlan.SCALE;
|
||||
default:
|
||||
return BillingPlan.PRO;
|
||||
}
|
||||
export function isFreeTier(tier: Tier) {
|
||||
return get(plansInfo).get(tier).order === 0;
|
||||
}
|
||||
|
||||
export function getPreviousTier(tier: Tier) {
|
||||
switch (tier) {
|
||||
case BillingPlan.PRO:
|
||||
return BillingPlan.FREE;
|
||||
case BillingPlan.SCALE:
|
||||
return BillingPlan.PRO;
|
||||
default:
|
||||
return BillingPlan.FREE;
|
||||
}
|
||||
export function isGithubEducationTier(tier: Tier) {
|
||||
return get(plansInfo).get(tier).order === 1;
|
||||
}
|
||||
|
||||
export type PlanServices =
|
||||
@@ -170,42 +141,6 @@ export const failedInvoice = cachedStore<
|
||||
|
||||
export const actionRequiredInvoices = writable<InvoiceList>(null);
|
||||
|
||||
export type TierData = {
|
||||
name: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export const tierFree: TierData = {
|
||||
name: 'Free',
|
||||
description: 'A great fit for passion projects and small applications.'
|
||||
};
|
||||
|
||||
export const tierGitHubEducation: TierData = {
|
||||
name: 'GitHub Education',
|
||||
description: 'For members of GitHub student developers program.'
|
||||
};
|
||||
|
||||
export const tierPro: TierData = {
|
||||
name: 'Pro',
|
||||
description:
|
||||
'For production applications that need powerful functionality and resources to scale.'
|
||||
};
|
||||
export const tierScale: TierData = {
|
||||
name: 'Scale',
|
||||
description:
|
||||
'For teams that handle more complex and large projects and need more control and support.'
|
||||
};
|
||||
|
||||
export const tierCustom: TierData = {
|
||||
name: 'Custom',
|
||||
description: 'Team on a custom contract'
|
||||
};
|
||||
|
||||
export const tierEnterprise: TierData = {
|
||||
name: 'Enterprise',
|
||||
description: 'For enterprises that need more power and premium support.'
|
||||
};
|
||||
|
||||
export const showUsageRatesModal = writable<boolean>(false);
|
||||
|
||||
export function checkForUsageFees(plan: Tier, id: PlanServices) {
|
||||
@@ -249,7 +184,7 @@ export function isServiceLimited(serviceId: PlanServices, plan: Tier, total: num
|
||||
}
|
||||
|
||||
export function calculateTrialDay(org: Organization) {
|
||||
if (org?.billingPlan === BillingPlan.FREE) return false;
|
||||
if (isFreeTier(org?.billingPlan)) return false;
|
||||
const endDate = new Date(org?.billingStartDate);
|
||||
const today = new Date();
|
||||
|
||||
@@ -263,7 +198,7 @@ export function calculateTrialDay(org: Organization) {
|
||||
}
|
||||
|
||||
export async function checkForUsageLimit(org: Organization) {
|
||||
if (org?.billingPlan !== BillingPlan.FREE) {
|
||||
if (!isFreeTier(org?.billingPlan)) {
|
||||
readOnly.set(false);
|
||||
return;
|
||||
}
|
||||
@@ -334,7 +269,7 @@ export async function checkForUsageLimit(org: Organization) {
|
||||
}
|
||||
|
||||
export async function checkPaymentAuthorizationRequired(org: Organization) {
|
||||
if (org.billingPlan === BillingPlan.FREE) return;
|
||||
if (isFreeTier(org.billingPlan)) return;
|
||||
|
||||
const invoices = await sdk.forConsole.billing.listInvoices(org.$id, [
|
||||
Query.equal('status', 'requires_authentication')
|
||||
@@ -450,7 +385,7 @@ export async function checkForMissingPaymentMethod() {
|
||||
|
||||
// Display upgrade banner for new users after 1 week for 30 days
|
||||
export async function checkForNewDevUpgradePro(org: Organization) {
|
||||
if (org?.billingPlan !== BillingPlan.FREE || !browser) return;
|
||||
if (!isFreeTier(org?.billingPlan) || !browser) return;
|
||||
|
||||
const orgs = await sdk.forConsole.billing.listOrganization([
|
||||
Query.notEqual('billingPlan', BillingPlan.FREE)
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
import { specificationsList } from '$lib/stores/specifications';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import SpecificationsTooltip from '../components/specificationsTooltip.svelte';
|
||||
import { isFreeTier } from '$lib/stores/billing';
|
||||
|
||||
let showCustomId = false;
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
required
|
||||
disabled={specificationOptions.length < 1}
|
||||
options={specificationOptions}
|
||||
popover={isCloud && $organization?.billingPlan === BillingPlan.FREE
|
||||
popover={isCloud && isFreeTier($organization?.billingPlan)
|
||||
? SpecificationsTooltip
|
||||
: null}
|
||||
bind:value={$createFunction.specification} />
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { runtimesList } from '$lib/stores/runtimes';
|
||||
import { specificationsList } from '$lib/stores/specifications';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import SpecificationsTooltip from '../components/specificationsTooltip.svelte';
|
||||
import { template, templateConfig } from '../store';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { isFreeTier } from '$lib/stores/billing';
|
||||
|
||||
let showCustomId = false;
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
required
|
||||
disabled={specificationOptions.length < 1}
|
||||
options={specificationOptions}
|
||||
popover={isCloud && $organization?.billingPlan === BillingPlan.FREE
|
||||
popover={isCloud && isFreeTier($organization?.billingPlan)
|
||||
? SpecificationsTooltip
|
||||
: null}
|
||||
bind:value={$templateConfig.specification} />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { BillingPlan, INTERVAL } from '$lib/constants';
|
||||
import { INTERVAL } from '$lib/constants';
|
||||
import { Logs } from '$lib/layout';
|
||||
import Footer from '$lib/layout/footer.svelte';
|
||||
import Header from '$lib/layout/header.svelte';
|
||||
@@ -23,6 +23,7 @@
|
||||
checkForNewDevUpgradePro,
|
||||
checkForUsageLimit,
|
||||
checkPaymentAuthorizationRequired,
|
||||
isFreeTier,
|
||||
paymentExpired,
|
||||
plansInfo,
|
||||
showUsageRatesModal
|
||||
@@ -284,7 +285,7 @@
|
||||
checkForMarkedForDeletion(org);
|
||||
await checkForNewDevUpgradePro(org);
|
||||
|
||||
if (org?.billingPlan !== BillingPlan.FREE) {
|
||||
if (!isFreeTier(org?.billingPlan)) {
|
||||
await paymentExpired(org);
|
||||
await checkPaymentAuthorizationRequired(org);
|
||||
await checkForMandate(org);
|
||||
|
||||
@@ -17,10 +17,15 @@
|
||||
import { Pill } from '$lib/elements';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import type { Organization } from '$lib/stores/organization';
|
||||
import { daysLeftInTrial, plansInfo, tierToPlan } from '$lib/stores/billing';
|
||||
import {
|
||||
daysLeftInTrial,
|
||||
isFreeTier,
|
||||
isGithubEducationTier,
|
||||
plansInfo,
|
||||
tierToPlan
|
||||
} from '$lib/stores/billing';
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
import { toLocaleDate } from '$lib/helpers/date';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
export let data: PageData;
|
||||
@@ -71,7 +76,7 @@
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="status">
|
||||
{#if isCloudOrg(organization)}
|
||||
{#if organization?.billingPlan === BillingPlan.FREE || organization?.billingPlan === BillingPlan.GITHUB_EDUCATION}
|
||||
{#if isFreeTier(organization?.billingPlan) || isGithubEducationTier(organization?.billingPlan)}
|
||||
<div
|
||||
class="u-flex u-cross-center"
|
||||
use:tooltip={{
|
||||
@@ -82,7 +87,7 @@
|
||||
>{tierToPlan(organization?.billingPlan)?.name}</Pill>
|
||||
</div>
|
||||
{/if}
|
||||
{#if organization?.billingTrialStartDate && $daysLeftInTrial > 0 && organization.billingPlan !== BillingPlan.FREE && $plansInfo.get(organization.billingPlan)?.trialDays}
|
||||
{#if organization?.billingTrialStartDate && $daysLeftInTrial > 0 && !isFreeTier(organization.billingPlan) && $plansInfo.get(organization.billingPlan)?.trialDays}
|
||||
<div
|
||||
class="u-flex u-cross-center"
|
||||
use:tooltip={{
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
} from '$lib/layout';
|
||||
import { type PaymentList } from '$lib/sdk/billing';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { isOrganization } from '$lib/stores/billing.js';
|
||||
import { isFreeTier, isGithubEducationTier, isOrganization } from '$lib/stores/billing.js';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import {
|
||||
organizationList,
|
||||
@@ -236,7 +236,7 @@
|
||||
placeholder="Select organization"
|
||||
id="organization" />
|
||||
{/if}
|
||||
{#if selectedOrgId && (selectedOrg?.billingPlan === BillingPlan.FREE || !selectedOrg?.paymentMethodId)}
|
||||
{#if selectedOrgId && (isFreeTier(selectedOrg?.billingPlan) || !selectedOrg?.paymentMethodId)}
|
||||
{#if selectedOrgId === newOrgId}
|
||||
<InputText
|
||||
label="Organization name"
|
||||
@@ -295,7 +295,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if selectedOrg?.$id && selectedOrg?.billingPlan !== BillingPlan.FREE && selectedOrg?.billingPlan !== BillingPlan.GITHUB_EDUCATION}
|
||||
{#if selectedOrg?.$id && !isFreeTier(selectedOrg?.billingPlan) && !isGithubEducationTier(selectedOrg?.billingPlan)}
|
||||
<section
|
||||
class="card u-margin-block-start-24"
|
||||
style:--p-card-padding="1.5rem"
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
WizardSecondaryFooter
|
||||
} from '$lib/layout';
|
||||
import type { Coupon, PaymentList } from '$lib/sdk/billing';
|
||||
import { isOrganization, tierToPlan } from '$lib/stores/billing';
|
||||
import { isFreeTier, isOrganization, tierToPlan } from '$lib/stores/billing';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import {
|
||||
organizationList,
|
||||
@@ -28,8 +28,8 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
$: anyOrgFree = $organizationList.teams?.some(
|
||||
(org) => (org as Organization)?.billingPlan === BillingPlan.FREE
|
||||
$: anyOrgFree = $organizationList.teams?.some((org) =>
|
||||
isFreeTier((org as Organization)?.billingPlan)
|
||||
);
|
||||
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$/i;
|
||||
let previousPage: string = base;
|
||||
@@ -126,7 +126,7 @@
|
||||
try {
|
||||
let org: Organization | OrganizationError;
|
||||
|
||||
if (billingPlan === BillingPlan.FREE) {
|
||||
if (isFreeTier(billingPlan)) {
|
||||
org = await sdk.forConsole.billing.createOrganization(
|
||||
ID.unique(),
|
||||
name,
|
||||
@@ -192,7 +192,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: if (billingPlan !== BillingPlan.FREE) {
|
||||
$: if (!isFreeTier(billingPlan)) {
|
||||
loadPaymentMethods();
|
||||
}
|
||||
</script>
|
||||
@@ -219,7 +219,7 @@
|
||||
<Button href="https://appwrite.io/pricing" external link>pricing page</Button>.
|
||||
</p>
|
||||
<SelectPlan bind:billingPlan {anyOrgFree} isNewOrg />
|
||||
{#if billingPlan !== BillingPlan.FREE}
|
||||
{#if !isFreeTier(billingPlan)}
|
||||
<FormList class="u-margin-block-start-24">
|
||||
<InputTags
|
||||
bind:tags={collaborators}
|
||||
@@ -244,7 +244,7 @@
|
||||
{/if}
|
||||
</Form>
|
||||
<svelte:fragment slot="aside">
|
||||
{#if billingPlan !== BillingPlan.FREE}
|
||||
{#if !isFreeTier(billingPlan)}
|
||||
<EstimatedTotal bind:billingBudget {billingPlan} {collaborators} bind:couponData />
|
||||
{:else}
|
||||
<PlanComparisonBox />
|
||||
|
||||
@@ -12,7 +12,13 @@
|
||||
import { isCloud } from '$lib/system';
|
||||
import { ID } from '@appwrite.io/console';
|
||||
import { onMount } from 'svelte';
|
||||
import { tierToPlan, type Tier, plansInfo, isOrganization } from '$lib/stores/billing';
|
||||
import {
|
||||
tierToPlan,
|
||||
type Tier,
|
||||
plansInfo,
|
||||
isOrganization,
|
||||
isFreeTier
|
||||
} from '$lib/stores/billing';
|
||||
import { formatCurrency } from '$lib/helpers/numbers';
|
||||
import { base } from '$app/paths';
|
||||
import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect';
|
||||
@@ -46,7 +52,7 @@
|
||||
async function handleSubmit() {
|
||||
const orgName = name?.length ? name : 'Personal Projects';
|
||||
if (isCloud) {
|
||||
if (plan === BillingPlan.FREE) {
|
||||
if (isFreeTier(plan)) {
|
||||
try {
|
||||
const org = await sdk.forConsole.billing.createOrganization(
|
||||
ID.unique(),
|
||||
|
||||
@@ -19,9 +19,8 @@
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import AddCreditModal from './addCreditModal.svelte';
|
||||
import { formatCurrency } from '$lib/helpers/numbers';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { upgradeURL } from '$lib/stores/billing';
|
||||
import { isFreeTier, upgradeURL } from '$lib/stores/billing';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
|
||||
@@ -89,14 +88,14 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<CardGrid hideFooter={$organization?.billingPlan !== BillingPlan.FREE}>
|
||||
<CardGrid hideFooter={!isFreeTier($organization?.billingPlan)}>
|
||||
<Heading tag="h2" size="6">
|
||||
{$organization?.billingPlan === BillingPlan.FREE ? 'Credits' : 'Available credit'}
|
||||
{isFreeTier($organization?.billingPlan) ? 'Credits' : 'Available credit'}
|
||||
</Heading>
|
||||
|
||||
<p class="text">Appwrite credit will automatically be applied to your next invoice.</p>
|
||||
<svelte:fragment slot="aside">
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier($organization?.billingPlan)}
|
||||
<Alert type="info">
|
||||
<svelte:fragment slot="title">Upgrade to Pro to add credits</svelte:fragment>
|
||||
Upgrade to a Pro plan to add credits to your organization. For more information on what
|
||||
@@ -173,7 +172,7 @@
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier($organization?.billingPlan)}
|
||||
<Button
|
||||
secondary
|
||||
href={$upgradeURL}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Alert, CardGrid, Heading } from '$lib/components';
|
||||
import { BillingPlan, Dependencies } from '$lib/constants';
|
||||
import { tierToPlan, upgradeURL } from '$lib/stores/billing';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { isFreeTier, isGithubEducationTier, tierToPlan, upgradeURL } from '$lib/stores/billing';
|
||||
import { Button, Form, FormList, InputSelectSearch } from '$lib/elements/forms';
|
||||
import {
|
||||
Table,
|
||||
@@ -163,7 +163,7 @@
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE || $organization?.billingPlan === BillingPlan.GITHUB_EDUCATION}
|
||||
{#if isFreeTier($organization?.billingPlan) || isGithubEducationTier($organization?.billingPlan)}
|
||||
<Button
|
||||
secondary
|
||||
href={$upgradeURL}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Alert, CardGrid, Heading } from '$lib/components';
|
||||
import { BillingPlan, Dependencies } from '$lib/constants';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, FormList, InputNumber, InputSwitch } from '$lib/elements/forms';
|
||||
import { showUsageRatesModal, upgradeURL } from '$lib/stores/billing';
|
||||
import { isFreeTier, showUsageRatesModal, upgradeURL } from '$lib/stores/billing';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { organization, currentPlan } from '$lib/stores/organization';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
@@ -96,7 +96,7 @@
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier($organization?.billingPlan)}
|
||||
<Button
|
||||
secondary
|
||||
href={$upgradeURL}
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { BillingPlan, Dependencies } from '$lib/constants';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { isFreeTier } from '$lib/stores/billing.js';
|
||||
|
||||
export let showDelete = false;
|
||||
export let isBackup = false;
|
||||
@@ -16,7 +17,7 @@
|
||||
let error: string;
|
||||
|
||||
async function removeDefaultMethod() {
|
||||
if ($organization?.billingPlan !== BillingPlan.FREE && !hasOtherMethod) return;
|
||||
if (!isFreeTier($organization?.billingPlan) && !hasOtherMethod) return;
|
||||
|
||||
try {
|
||||
await sdk.forConsole.billing.removeOrganizationPaymentMethod($organization.$id);
|
||||
@@ -35,7 +36,7 @@
|
||||
}
|
||||
}
|
||||
async function removeBackuptMethod() {
|
||||
if ($organization?.billingPlan !== BillingPlan.FREE && !hasOtherMethod) return;
|
||||
if (!isFreeTier($organization?.billingPlan) && !hasOtherMethod) return;
|
||||
showDelete = false;
|
||||
|
||||
try {
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
DropListItem,
|
||||
Heading
|
||||
} from '$lib/components';
|
||||
import { BillingPlan, Dependencies } from '$lib/constants';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { hasStripePublicKey, isCloud } from '$lib/system';
|
||||
import { paymentMethods } from '$lib/stores/billing';
|
||||
import { isFreeTier, isGithubEducationTier, paymentMethods } from '$lib/stores/billing';
|
||||
import type { PaymentMethodData } from '$lib/sdk/billing';
|
||||
import DeleteOrgPayment from './deleteOrgPayment.svelte';
|
||||
import ReplaceCard from './replaceCard.svelte';
|
||||
@@ -195,7 +195,7 @@
|
||||
</article>
|
||||
{/if}
|
||||
</div>
|
||||
{#if $organization?.billingPlan !== BillingPlan.FREE && $organization?.billingPlan !== BillingPlan.GITHUB_EDUCATION}
|
||||
{#if !isFreeTier($organization?.billingPlan) && !isGithubEducationTier($organization?.billingPlan)}
|
||||
<div class="u-flex u-flex-vertical u-gap-8">
|
||||
{#if $organization?.backupPaymentMethodId}
|
||||
<h4 class="u-bold body-text-2">Backup</h4>
|
||||
@@ -330,5 +330,5 @@
|
||||
bind:showDelete
|
||||
{hasOtherMethod}
|
||||
isBackup={isSelectedBackup}
|
||||
disabled={$organization?.billingPlan !== BillingPlan.FREE && !hasOtherMethod} />
|
||||
disabled={!isFreeTier($organization?.billingPlan) && !hasOtherMethod} />
|
||||
{/if}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Card, CardGrid, Collapsible, CollapsibleItem, Heading } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { toLocaleDate } from '$lib/helpers/date';
|
||||
import { plansInfo, upgradeURL } from '$lib/stores/billing';
|
||||
import { isFreeTier, isGithubEducationTier, plansInfo, upgradeURL } from '$lib/stores/billing';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import type { Aggregation, CreditList, Invoice, Plan } from '$lib/sdk/billing';
|
||||
import { abbreviateNumber, formatCurrency, formatNumberWithCommas } from '$lib/helpers/numbers';
|
||||
@@ -46,8 +46,7 @@
|
||||
<div class="u-flex">
|
||||
<span class="body-text-2">{currentPlan.name} plan</span>
|
||||
<div class="body-text-2 u-margin-inline-start-auto">
|
||||
{isTrial ||
|
||||
$organization?.billingPlan === BillingPlan.GITHUB_EDUCATION
|
||||
{isTrial || isGithubEducationTier($organization?.billingPlan)
|
||||
? formatCurrency(0)
|
||||
: currentPlan
|
||||
? formatCurrency(currentPlan?.price)
|
||||
@@ -173,7 +172,7 @@
|
||||
</CollapsibleItem>
|
||||
{/if}
|
||||
|
||||
{#if $organization?.billingPlan !== BillingPlan.FREE && $organization?.billingPlan !== BillingPlan.GITHUB_EDUCATION}
|
||||
{#if !isFreeTier($organization?.billingPlan) && !isGithubEducationTier($organization?.billingPlan)}
|
||||
<CollapsibleItem
|
||||
noContent
|
||||
gap={4}
|
||||
@@ -208,7 +207,7 @@
|
||||
</Card>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE || $organization?.billingPlan === BillingPlan.GITHUB_EDUCATION}
|
||||
{#if isFreeTier($organization?.billingPlan) || isGithubEducationTier($organization?.billingPlan)}
|
||||
<div
|
||||
class="u-flex u-flex-vertical-mobile u-cross-center u-gap-16 u-flex-wrap u-width-full-line u-main-end">
|
||||
<Button text href={`${base}/organization-${$organization?.$id}/usage`}>
|
||||
|
||||
@@ -28,7 +28,13 @@
|
||||
WizardSecondaryFooter
|
||||
} from '$lib/layout';
|
||||
import { type Coupon, type PaymentList } from '$lib/sdk/billing';
|
||||
import { isOrganization, plansInfo, tierToPlan, type Tier } from '$lib/stores/billing';
|
||||
import {
|
||||
isOrganization,
|
||||
plansInfo,
|
||||
tierToPlan,
|
||||
type Tier,
|
||||
isFreeTier
|
||||
} from '$lib/stores/billing';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import {
|
||||
currentPlan,
|
||||
@@ -45,8 +51,8 @@
|
||||
|
||||
export let data;
|
||||
|
||||
$: anyOrgFree = $organizationList.teams?.find(
|
||||
(org) => (org as Organization)?.billingPlan === BillingPlan.FREE
|
||||
$: anyOrgFree = $organizationList.teams?.find((org) =>
|
||||
isFreeTier((org as Organization)?.billingPlan)
|
||||
);
|
||||
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$/i;
|
||||
let previousPage: string = base;
|
||||
@@ -276,7 +282,7 @@
|
||||
|
||||
$: isUpgrade = $plansInfo.get(billingPlan).order > $currentPlan.order;
|
||||
$: isDowngrade = $plansInfo.get(billingPlan).order < $currentPlan.order;
|
||||
$: if (billingPlan !== BillingPlan.FREE) {
|
||||
$: if (isFreeTier(billingPlan)) {
|
||||
loadPaymentMethods();
|
||||
}
|
||||
$: isButtonDisabled = ($currentPlan?.$id as Tier) === billingPlan;
|
||||
@@ -303,7 +309,7 @@
|
||||
<SelectPlan bind:billingPlan anyOrgFree={!!anyOrgFree} class="u-margin-block-16" />
|
||||
|
||||
{#if isDowngrade}
|
||||
{#if billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier(billingPlan)}
|
||||
<PlanExcess
|
||||
tier={BillingPlan.FREE}
|
||||
class="u-margin-block-start-24"
|
||||
@@ -328,7 +334,7 @@
|
||||
{/if}
|
||||
{/if}
|
||||
<!-- Show email input if upgrading from free plan -->
|
||||
{#if billingPlan !== BillingPlan.FREE && $organization.billingPlan !== billingPlan && $organization.billingPlan !== BillingPlan.CUSTOM && isUpgrade}
|
||||
{#if $organization.billingPlan !== billingPlan && tierToPlan(billingPlan)?.addons.seats?.supported && isUpgrade}
|
||||
<FormList class="u-margin-block-start-16">
|
||||
<InputTags
|
||||
bind:tags={collaborators}
|
||||
@@ -350,7 +356,8 @@
|
||||
</Button>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if isDowngrade && billingPlan === BillingPlan.FREE}
|
||||
<!-- TODO: how do we check against the free plan -->
|
||||
{#if isDowngrade && isFreeTier(billingPlan)}
|
||||
<FormList class="u-margin-block-start-24">
|
||||
<InputSelect
|
||||
id="reason"
|
||||
@@ -369,13 +376,16 @@
|
||||
{/if}
|
||||
</Form>
|
||||
<svelte:fragment slot="aside">
|
||||
{#if billingPlan !== BillingPlan.FREE && $organization.billingPlan !== billingPlan && $organization.billingPlan !== BillingPlan.CUSTOM && isUpgrade}
|
||||
<!-- TODO: we need a way to check for custom plans -->
|
||||
{#if $organization.billingPlan !== billingPlan && $organization.billingPlan !== BillingPlan.CUSTOM && isUpgrade}
|
||||
<EstimatedTotal
|
||||
bind:billingBudget
|
||||
bind:couponData
|
||||
organizationId={$organization.$id}
|
||||
{billingPlan}
|
||||
{collaborators} />
|
||||
|
||||
<!-- TODO: we need a way to check for custom plans -->
|
||||
{:else if $organization.billingPlan !== BillingPlan.CUSTOM}
|
||||
<PlanComparisonBox downgrade={isDowngrade} />
|
||||
{/if}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
Tab,
|
||||
Tabs
|
||||
} from '$lib/components';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { toLocaleDate } from '$lib/helpers/date';
|
||||
@@ -21,6 +20,8 @@
|
||||
import {
|
||||
daysLeftInTrial,
|
||||
getServiceLimit,
|
||||
isFreeTier,
|
||||
isGithubEducationTier,
|
||||
plansInfo,
|
||||
readOnly,
|
||||
tierToPlan
|
||||
@@ -110,15 +111,15 @@
|
||||
<span class="u-trim">
|
||||
{$organization.name}
|
||||
</span>
|
||||
{#if isCloud && $organization?.billingPlan === BillingPlan.GITHUB_EDUCATION}
|
||||
{#if isCloud && isGithubEducationTier($organization?.billingPlan)}
|
||||
<Pill class="eyebrow-heading-3" style="--p-tag-content-height:2rem">
|
||||
<span class="icon-github" aria-hidden="true" />EDUCATION
|
||||
</Pill>
|
||||
{:else if isCloud && $organization?.billingPlan === BillingPlan.FREE}
|
||||
{:else if isCloud && isFreeTier($organization?.billingPlan)}
|
||||
<Pill class="eyebrow-heading-3" style="--p-tag-content-height:2rem"
|
||||
>FREE</Pill>
|
||||
{/if}
|
||||
{#if isCloud && $organization?.billingTrialStartDate && $daysLeftInTrial > 0 && $organization.billingPlan !== BillingPlan.FREE && $plansInfo.get($organization.billingPlan)?.trialDays}
|
||||
{#if isCloud && $organization?.billingTrialStartDate && $daysLeftInTrial > 0 && !isFreeTier($organization.billingPlan) && $plansInfo.get($organization.billingPlan)?.trialDays}
|
||||
<div
|
||||
class="u-flex u-cross-center"
|
||||
use:tooltip={{
|
||||
@@ -162,12 +163,11 @@
|
||||
</a>
|
||||
<div
|
||||
use:tooltip={{
|
||||
content:
|
||||
$organization?.billingPlan === BillingPlan.FREE
|
||||
? `Upgrade to add more members`
|
||||
: `You've reached the members limit for the ${
|
||||
tierToPlan($organization?.billingPlan)?.name
|
||||
} plan`,
|
||||
content: isFreeTier($organization?.billingPlan)
|
||||
? `Upgrade to add more members`
|
||||
: `You've reached the members limit for the ${
|
||||
tierToPlan($organization?.billingPlan)?.name
|
||||
} plan`,
|
||||
disabled: !areMembersLimited
|
||||
}}>
|
||||
{#if $isOwner}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { Card, CardGrid, Heading, ProgressBarBig } from '$lib/components';
|
||||
import {
|
||||
getServiceLimit,
|
||||
isFreeTier,
|
||||
showUsageRatesModal,
|
||||
type Tier,
|
||||
upgradeURL
|
||||
@@ -40,7 +41,7 @@
|
||||
<div class="u-flex u-cross-center u-main-space-between">
|
||||
<Heading tag="h2" size="5">Usage</Heading>
|
||||
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier($organization?.billingPlan)}
|
||||
<Button
|
||||
href={$upgradeURL}
|
||||
on:click={() => {
|
||||
@@ -70,7 +71,7 @@
|
||||
class="link"
|
||||
type="button">Learn more about plan usage limits.</button>
|
||||
</p>
|
||||
{:else if $organization.billingPlan === BillingPlan.FREE}
|
||||
{:else if isFreeTier($organization.billingPlan)}
|
||||
<p class="text">
|
||||
If you exceed the limits of the Free plan, services for your organization's projects
|
||||
may be disrupted.
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@
|
||||
} from '$lib/elements/table';
|
||||
import { toLocaleDate } from '$lib/helpers/date';
|
||||
import { formatCurrency } from '$lib/helpers/numbers';
|
||||
import { plansInfo, tierToPlan } from '$lib/stores/billing';
|
||||
import { isFreeTier, plansInfo, tierToPlan } from '$lib/stores/billing';
|
||||
import { newMemberModal, organization } from '$lib/stores/organization';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<p class="text">The number of members in your organization.</p>
|
||||
<svelte:fragment slot="aside">
|
||||
{#if $organization.billingPlan !== BillingPlan.FREE}
|
||||
{#if !isFreeTier($organization.billingPlan)}
|
||||
<div class="u-flex u-flex-vertical">
|
||||
<div class="u-flex u-main-space-between">
|
||||
<p>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<p class="text">Enable or disable Appwrite branding in your email template signature.</p>
|
||||
|
||||
<svelte:fragment slot="aside">
|
||||
<EmptyCardImageCloud source="email_signature_card" let:nextTier noAspectRatio>
|
||||
<EmptyCardImageCloud source="email_signature_card" noAspectRatio>
|
||||
<svelte:fragment slot="image">
|
||||
<div class=" is-only-mobile u-width-full-line u-height-100-percent">
|
||||
{#if $app.themeInUse === 'dark'}
|
||||
@@ -51,7 +51,8 @@
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="title">Upgrade to edit email signature</svelte:fragment>
|
||||
Upgrade to a {nextTier} plan to enable or disable Appwrite branding in your emails.
|
||||
<!-- Confirm copy of paid plan -->
|
||||
Upgrade to a paid plan to enable or disable Appwrite branding in your emails.
|
||||
|
||||
<svelte:fragment let:source slot="cta">
|
||||
<Button
|
||||
|
||||
@@ -8,9 +8,8 @@
|
||||
import { ID } from '@appwrite.io/console';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { upgradeURL } from '$lib/stores/billing';
|
||||
import { isFreeTier, upgradeURL } from '$lib/stores/billing';
|
||||
import CreatePolicy from './database-[database]/backups/createPolicy.svelte';
|
||||
import { cronExpression, type UserBackupPolicy } from '$lib/helpers/backups';
|
||||
|
||||
@@ -126,7 +125,7 @@
|
||||
|
||||
{#if isCloud}
|
||||
<div class="u-flex-vertical u-gap-24 u-padding-block-start-24">
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier($organization?.billingPlan)}
|
||||
{#if showPlanUpgradeAlert}
|
||||
<Alert
|
||||
type="warning"
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Alert, EmptySearch, PaginationWithLimit, ViewSelector } from '$lib/components';
|
||||
import { BillingPlan, Dependencies } from '$lib/constants';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { hoursToDays } from '$lib/helpers/date';
|
||||
import { Container, ContainerHeader } from '$lib/layout';
|
||||
@@ -9,7 +9,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { func } from '../store';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { getServiceLimit, showUsageRatesModal } from '$lib/stores/billing';
|
||||
import { getServiceLimit, isFreeTier, showUsageRatesModal } from '$lib/stores/billing';
|
||||
import { project } from '$routes/(console)/project-[project]/store';
|
||||
import Create from '../create.svelte';
|
||||
import { abbreviateNumber } from '$lib/helpers/numbers';
|
||||
@@ -171,7 +171,7 @@
|
||||
{hoursToDays(logs)} of logs
|
||||
</li>
|
||||
</ul>
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier($organization?.billingPlan)}
|
||||
<p class="text">
|
||||
<button class="link" type="button" on:click|preventDefault={upgradeMethod}
|
||||
>Upgrade</button>
|
||||
|
||||
+3
-2
@@ -3,7 +3,7 @@
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { BillingPlan, Dependencies } from '$lib/constants';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, FormList } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
@@ -17,6 +17,7 @@
|
||||
import { isCloud } from '$lib/system';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import SpecificationsTooltip from '$lib/wizards/functions/components/specificationsTooltip.svelte';
|
||||
import { isFreeTier } from '$lib/stores/billing';
|
||||
|
||||
const functionId = $page.params.function;
|
||||
let runtime: string = null;
|
||||
@@ -108,7 +109,7 @@
|
||||
placeholder="Select runtime specification"
|
||||
bind:value={specification}
|
||||
options={specificationOptions}
|
||||
popover={isCloud && $organization?.billingPlan === BillingPlan.FREE
|
||||
popover={isCloud && isFreeTier($organization?.billingPlan)
|
||||
? SpecificationsTooltip
|
||||
: null}
|
||||
required
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import InputPassword from '$lib/elements/forms/inputPassword.svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { BillingPlan, Dependencies } from '$lib/constants';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import InputNumber from '$lib/elements/forms/inputNumber.svelte';
|
||||
@@ -23,7 +23,7 @@
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { type SMTPSecure } from '@appwrite.io/console';
|
||||
import InputSelect from '$lib/elements/forms/inputSelect.svelte';
|
||||
import { upgradeURL } from '$lib/stores/billing';
|
||||
import { isFreeTier, upgradeURL } from '$lib/stores/billing';
|
||||
|
||||
let enabled = false;
|
||||
let senderName: string;
|
||||
@@ -132,7 +132,7 @@
|
||||
class="link">here</a>
|
||||
</p>
|
||||
<svelte:fragment slot="aside">
|
||||
{#if $organization.billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier($organization.billingPlan)}
|
||||
<Alert type="info">
|
||||
Custom SMTP is a Pro plan feature. Upgrade to enable custom SMTP sever.
|
||||
<svelte:fragment slot="action">
|
||||
@@ -204,9 +204,7 @@
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<Button
|
||||
submit
|
||||
disabled={isButtonDisabled || $organization.billingPlan === BillingPlan.FREE}>
|
||||
<Button submit disabled={isButtonDisabled || isFreeTier($organization.billingPlan)}>
|
||||
Update
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
Table,
|
||||
TableRowLink
|
||||
} from '$lib/elements/table';
|
||||
import { showUsageRatesModal, tierToPlan, upgradeURL } from '$lib/stores/billing';
|
||||
import { isFreeTier, showUsageRatesModal, tierToPlan, upgradeURL } from '$lib/stores/billing';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { bytesToSize, humanFileSize, mbSecondsToGBHours } from '$lib/helpers/sizeConvertion';
|
||||
@@ -66,7 +66,7 @@
|
||||
<div class="u-flex u-cross-center u-main-space-between">
|
||||
<Heading tag="h2" size="5">Usage</Heading>
|
||||
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier($organization?.billingPlan)}
|
||||
<Button href={$upgradeURL}>
|
||||
<span class="text">Upgrade</span>
|
||||
</Button>
|
||||
@@ -87,7 +87,7 @@
|
||||
on:click={() => ($showUsageRatesModal = true)}
|
||||
link>Learn more about plan usage limits.</Button>
|
||||
</p>
|
||||
{:else if $organization.billingPlan === BillingPlan.FREE}
|
||||
{:else if isFreeTier($organization.billingPlan)}
|
||||
<p class="text">
|
||||
If you exceed the limits of the {plan} plan, services for your projects may be disrupted.
|
||||
<a href={$upgradeURL} class="link">Upgrade for greater capacity</a>.
|
||||
|
||||
+3
-4
@@ -1,11 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { Submit } from '$lib/actions/analytics';
|
||||
import { Alert, CardGrid, Heading } from '$lib/components';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { Button, Form, FormItem, InputNumber, InputSelect } from '$lib/elements/forms';
|
||||
import { humanFileSize, sizeToBytes } from '$lib/helpers/sizeConvertion';
|
||||
import { createByteUnitPair } from '$lib/helpers/unit';
|
||||
import { readOnly, upgradeURL } from '$lib/stores/billing';
|
||||
import { isFreeTier, readOnly, upgradeURL } from '$lib/stores/billing';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { GRACE_PERIOD_OVERRIDE, isCloud } from '$lib/system';
|
||||
import { bucket } from '../store';
|
||||
@@ -47,12 +46,12 @@
|
||||
The {currentPlan.name} plan has a maximum upload file size limit of {Math.floor(
|
||||
parseInt(size.value)
|
||||
)}{size.unit}.
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier($organization?.billingPlan)}
|
||||
Upgrade to allow files of a larger size.
|
||||
{/if}
|
||||
</p>
|
||||
<svelte:fragment slot="action">
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
{#if isFreeTier($organization?.billingPlan)}
|
||||
<div class="alert-buttons u-flex">
|
||||
<Button text href={$upgradeURL}>Upgrade plan</Button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user