Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions e2e/steps/free-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ export async function createFreeProject(page: Page): Promise<Metadata> {
const regionPicker = dialog.locator('button[role="combobox"]');
if (await regionPicker.isVisible()) {
await regionPicker.click();
await page.getByRole('option', { name: /New York/i }).click();

region = 'nyc';
const firstEnabledOption = page
.locator('[role="option"]:not([data-disabled="true"])')
.first();

if ((await firstEnabledOption.count()) > 0) {
const selectedRegion = await firstEnabledOption.getAttribute('data-value');
await firstEnabledOption.click();
region = selectedRegion?.replace(/"/g, '') || 'fra';
}
}

await dialog.getByRole('button', { name: 'create' }).click();
Expand Down
10 changes: 8 additions & 2 deletions e2e/steps/pro-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,15 @@ export async function createProProject(page: Page): Promise<Metadata> {
const regionPicker = dialog.locator('button[role="combobox"]');
if (await regionPicker.isVisible()) {
await regionPicker.click();
await page.getByRole('option', { name: /New York/i }).click();
const firstEnabledOption = page
.locator('[role="option"]:not([data-disabled="true"])')
.first();

region = 'nyc';
if ((await firstEnabledOption.count()) > 0) {
const selectedRegion = await firstEnabledOption.getAttribute('data-value');
await firstEnabledOption.click();
region = selectedRegion?.replace(/"/g, '') || 'fra';
}
}

await dialog.getByRole('button', { name: 'create' }).click();
Expand Down
11 changes: 5 additions & 6 deletions src/lib/components/backupDatabaseAlert.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script lang="ts">
import { page } from '$app/state';
import { BillingPlan } from '$lib/constants';
import { Button } from '$lib/elements/forms';
import { organization } from '$lib/stores/organization';
import { HeaderAlert } from '$lib/layout';
Expand All @@ -18,14 +17,14 @@
</script>

{#if $showPolicyAlert && isCloud && $organization?.$id && page.url.pathname.match(/\/databases\/database-[^/]+$/)}
{@const isFreePlan = $organization?.billingPlan === BillingPlan.FREE}
{@const areBackupsAvailable = $organization?.billingPlanDetails.backupsEnabled}

{@const subtitle = isFreePlan
{@const subtitle = !areBackupsAvailable
? 'Upgrade your plan to ensure your data stays safe and backed up'
: 'Protect your data by quickly adding a backup policy'}

{@const ctaText = isFreePlan ? 'Upgrade plan' : 'Create policy'}
{@const ctaURL = isFreePlan ? $upgradeURL : `${page.url.pathname}/backups`}
{@const ctaText = !areBackupsAvailable ? 'Upgrade plan' : 'Create policy'}
{@const ctaURL = !areBackupsAvailable ? $upgradeURL : `${page.url.pathname}/backups`}

<HeaderAlert type="warning" title="Your database has no backup policy">
<svelte:fragment>{subtitle}</svelte:fragment>
Expand All @@ -35,7 +34,7 @@
href={ctaURL}
secondary
fullWidthMobile
event={isFreePlan ? 'backup_banner_upgrade' : 'backup_banner_add'}>
event={!areBackupsAvailable ? 'backup_banner_upgrade' : 'backup_banner_add'}>
<span class="text">{ctaText}</span>
</Button>

Expand Down
6 changes: 3 additions & 3 deletions src/lib/components/backupRestoreBox.svelte
Original file line number Diff line number Diff line change
Expand 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 { goto, invalidate } from '$app/navigation';
import { page } from '$app/state';
import { addNotification } from '$lib/stores/notifications';
Expand Down Expand Up @@ -125,8 +125,8 @@
}

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;
// fast path: don't subscribe if org doesn't support backups or is self-hosted.
if (isSelfHosted || (isCloud && !$organization?.billingPlanDetails.backupsEnabled)) return;

return realtime.forProject(page.params.region, 'console', (response) => {
if (!response.channels.includes(`projects.${getProjectId()}`)) return;
Expand Down
17 changes: 5 additions & 12 deletions src/lib/components/billing/alerts/limitReached.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,19 @@
import { base } from '$app/paths';
import { page } from '$app/state';
import { Click, trackEvent } from '$lib/actions/analytics';
import { BillingPlan } from '$lib/constants';
import { Button } from '$lib/elements/forms';
import { HeaderAlert } from '$lib/layout';
import {
hideBillingHeaderRoutes,
readOnly,
billingIdToPlan,
upgradeURL
} from '$lib/stores/billing';
import { hideBillingHeaderRoutes, readOnly, 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 && !$organization?.billingPlanDetails.usage && $readOnly && !hideBillingHeaderRoutes.includes(page.url.pathname)}
<HeaderAlert
type="error"
title={`${$organization.name} usage has reached the ${billingIdToPlan($organization.billingPlan).name} plan limit`}>
title={`${$organization.name} usage has reached the ${$organization.billingPlanDetails.name} plan limit`}>
<svelte:fragment>
Usage for the <b>{$organization.name}</b> organization has reached the limits of the {billingIdToPlan(
$organization.billingPlan
).name}
Usage for the <b>{$organization.name}</b> organization has reached the limits of the {$organization
.billingPlanDetails.name}
plan. Consider upgrading to increase your resource usage.
</svelte:fragment>
<svelte:fragment slot="buttons">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<script lang="ts">
import { base } from '$app/paths';
import { page } from '$app/state';
import { BillingPlan } from '$lib/constants';
import { Button } from '$lib/elements/forms';
import { HeaderAlert } from '$lib/layout';
import { hideBillingHeaderRoutes } from '$lib/stores/billing';
import { orgMissingPaymentMethod } from '$routes/(console)/store';
</script>

{#if ($orgMissingPaymentMethod.billingPlan === BillingPlan.PRO || $orgMissingPaymentMethod.billingPlan === BillingPlan.SCALE) && !$orgMissingPaymentMethod.paymentMethodId && !$orgMissingPaymentMethod.backupPaymentMethodId && !hideBillingHeaderRoutes.includes(page.url.pathname)}
{#if $orgMissingPaymentMethod.billingPlanDetails.requiresPaymentMethod && !$orgMissingPaymentMethod.paymentMethodId && !$orgMissingPaymentMethod.backupPaymentMethodId && !hideBillingHeaderRoutes.includes(page.url.pathname)}
<HeaderAlert
type="error"
title={`Payment method required for ${$orgMissingPaymentMethod.name}`}>
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/billing/alerts/newDevUpgradePro.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { base } from '$app/paths';
import { page } from '$app/state';
import { Click, 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';
Expand All @@ -23,7 +23,7 @@
}
</script>

{#if show && $organization?.$id && $organization?.billingPlan === BillingPlan.FREE && !page.url.pathname.includes(base + '/account')}
{#if show && $organization?.$id && !$organization?.billingPlanDetails.supportsCredits && !page.url.pathname.includes(base + '/account')}
<GradientBanner on:close={handleClose}>
<Layout.Stack
gap="m"
Expand Down
7 changes: 4 additions & 3 deletions src/lib/components/billing/estimatedTotalBox.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { AppwriteException, type Models } from '@appwrite.io/console';
import DiscountsApplied from './discountsApplied.svelte';

export let billingPlan: string;
export let billingPlan: Models.BillingPlan;
export let collaborators: string[];
export let couponData: Partial<Models.Coupon>;
export let billingBudget: number;
Expand Down Expand Up @@ -79,8 +79,8 @@
}

$: organizationId
? getUpdatePlanEstimate(organizationId, billingPlan, collaborators, couponData?.code)
: getEstimate(billingPlan, collaborators, couponData?.code);
? getUpdatePlanEstimate(organizationId, billingPlan.$id, collaborators, couponData?.code)
: getEstimate(billingPlan.$id, collaborators, couponData?.code);
</script>

{#if estimation}
Expand All @@ -103,6 +103,7 @@
{#if couponData?.status === 'active'}
<CreditsApplied bind:couponData {fixedCoupon} />
{/if}

<Divider />
<Layout.Stack direction="row" justifyContent="space-between">
<Typography.Text>Total due</Typography.Text>
Expand Down
1 change: 0 additions & 1 deletion src/lib/components/billing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ export { default as PlanComparisonBox } from './planComparisonBox.svelte';
export { default as EmptyCardCloud } from './emptyCardCloud.svelte';
export { default as CreditsApplied } from './creditsApplied.svelte';
export { default as PlanSelection } from './planSelection.svelte';
export { default as SelectPlan } from './selectPlan.svelte';
3 changes: 1 addition & 2 deletions src/lib/components/billing/planComparisonBox.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script lang="ts">
import { page } from '$app/state';
import { BillingPlan } from '$lib/constants';
import { formatNum } from '$lib/helpers/string';
import { BillingPlanGroup, type Models } from '@appwrite.io/console';
import { Card, Layout, Tabs, Typography } from '@appwrite.io/pink-svelte';
Expand All @@ -17,7 +16,7 @@
const currentPlan: Models.BillingPlan = $derived($plansInfo.get(selectedTab));
const visiblePlans: Array<Models.BillingPlan> = $derived.by(() => {
return page.data.plans.plans.filter(
(plan: Models.BillingPlan) => plan.$id !== BillingPlan.SCALE
(plan: Models.BillingPlan) => plan.group !== BillingPlanGroup.Scale
);
});

Expand Down
54 changes: 34 additions & 20 deletions src/lib/components/billing/planSelection.svelte
Original file line number Diff line number Diff line change
@@ -1,41 +1,55 @@
<script lang="ts">
import { BillingPlan } from '$lib/constants';
import { LabelCard } from '..';
import { page } from '$app/state';
import { formatCurrency } from '$lib/helpers/numbers';
import { currentPlan, organization } from '$lib/stores/organization';
import { BillingPlanGroup, type Models } from '@appwrite.io/console';
import { Badge, Layout, Tooltip, Typography } from '@appwrite.io/pink-svelte';
import { LabelCard } from '..';
import { page } from '$app/state';
import type { Models } from '@appwrite.io/console';
import { billingIdToPlan } from '$lib/stores/billing';

export let billingPlan: string;
export let isNewOrg = false;
export let selfService = true;
export let anyOrgFree = false;
let {
isNewOrg = false,
selfService = true,
anyOrgFree = false,
selectedBillingPlan = $bindable()
}: {
isNewOrg?: boolean;
selfService?: boolean;
anyOrgFree?: boolean;
selectedBillingPlan: Models.BillingPlan;
} = $props();

$: plans = Object.values(page.data.plans.plans) as Models.BillingPlan[];
$: currentPlanInList = plans.some((plan) => plan.$id === $currentPlan?.$id);
let selectedPlan = $state(selectedBillingPlan.$id);

// experiment to remove scale plan temporarily
$: plansWithoutScale = plans.filter((plan) => plan.$id != BillingPlan.SCALE);
const visiblePlans = $derived(Object.values(page.data.plans.plans) as Models.BillingPlan[]);
const currentPlanInList = $derived(visiblePlans.some((plan) => plan.$id === $currentPlan?.$id));

function shouldShowTooltip(plan: Models.BillingPlan) {
if (plan.$id !== BillingPlan.FREE) return true;
if (plan.group !== BillingPlanGroup.Starter) return true;
else return !anyOrgFree;
}

function shouldDisable(plan: Models.BillingPlan) {
return plan.group === BillingPlanGroup.Starter && anyOrgFree;
}

$effect(() => {
selectedBillingPlan = billingIdToPlan(selectedPlan);
});
</script>

<Layout.Stack>
{#each plansWithoutScale as plan}
{#each visiblePlans as plan}
<Tooltip disabled={shouldShowTooltip(plan)} maxWidth="fit-content">
<LabelCard
name="plan"
bind:group={billingPlan}
disabled={!selfService || (plan.$id === BillingPlan.FREE && anyOrgFree)}
tooltipShow={plan.$id === BillingPlan.FREE && anyOrgFree}
bind:group={selectedPlan}
disabled={!selfService || shouldDisable(plan)}
tooltipShow={shouldDisable(plan)}
value={plan.$id}
title={plan.name}>
<svelte:fragment slot="action">
{#if $organization?.billingPlan === plan.$id && !isNewOrg}
{#if $organization?.billingPlanId === plan.$id && !isNewOrg}
<Badge variant="secondary" size="xs" content="Current plan" />
{/if}
</svelte:fragment>
Expand All @@ -57,11 +71,11 @@
{#if $currentPlan && !currentPlanInList}
<LabelCard
name="plan"
bind:group={billingPlan}
bind:group={selectedPlan}
value={$currentPlan.$id}
title={$currentPlan.name}>
<svelte:fragment slot="action">
{#if $organization?.billingPlan === $currentPlan.$id && !isNewOrg}
{#if $organization?.billingPlanId === $currentPlan.$id && !isNewOrg}
<Badge variant="secondary" size="xs" content="Current plan" />
{/if}
</svelte:fragment>
Expand Down
50 changes: 0 additions & 50 deletions src/lib/components/billing/selectPlan.svelte

This file was deleted.

Loading