Merge pull request #2682 from appwrite/update-credits

This commit is contained in:
Darshan
2025-12-08 19:17:58 +05:30
committed by GitHub
23 changed files with 115 additions and 95 deletions
@@ -92,8 +92,8 @@
<Modal bind:show={showSelectProject} title="Manage projects" onSubmit={updateSelected}>
<svelte:fragment slot="description">
Choose which {$currentPlan?.projects || 2} projects to keep. Projects over the limit will be
blocked after this date.
Choose which {$currentPlan?.projects || 2} projects to keep. Projects over the limit will be blocked
after this date.
</svelte:fragment>
{#if loading}
+2 -2
View File
@@ -143,8 +143,8 @@
{:else}
<Card padding="xs" radius="s">
<Typography.Text>
Body data is not captured by {resolvedProfile.platform} for your user's security
and privacy. To display body data in the Logs tab, use <InlineCode
Body data is not captured by {resolvedProfile.platform} for your user's security and
privacy. To display body data in the Logs tab, use <InlineCode
code="context.log()"
size="s" />. <Link external {href} variant="muted">Learn more</Link>.
</Typography.Text>
@@ -129,8 +129,8 @@
{#if report && !isVersionAtLeast(version, '1.4.0') && $provider.provider === 'appwrite'}
<Alert.Inline status="warning">
<svelte:fragment slot="title">Functions not available for import</svelte:fragment>
To migrate your functions, update the version of the {resolvedProfile.platform} instance
you're importing from to a version newer than 1.4
To migrate your functions, update the version of the {resolvedProfile.platform} instance you're
importing from to a version newer than 1.4
</Alert.Inline>
{/if}
@@ -81,8 +81,8 @@
<svelte:fragment slot="title">Billing alerts</svelte:fragment>
{#if !currentPlan.budgeting}
Get notified by email when your organization meets a percentage of your budget cap. <b
>{tierToPlan(organization.billingPlan).name} organizations will receive one notification
at 75% resource usage.</b>
>{tierToPlan(organization.billingPlan).name} organizations will receive one notification at
75% resource usage.</b>
{:else}
Get notified by email when your organization meets or exceeds a percentage of your specified
billing alert(s).
@@ -50,8 +50,8 @@
<Form onSubmit={updateBudget}>
<CardGrid>
<svelte:fragment slot="title">Budget cap</svelte:fragment>
Restrict your resource usage by setting a budget cap. Cap usage is reset at the beginning of
each billing cycle.
Restrict your resource usage by setting a budget cap. Cap usage is reset at the beginning of each
billing cycle.
<svelte:fragment slot="aside">
{#if !currentPlan.budgeting}
<Alert.Inline status="info" title="Budget caps are a Pro plan feature">
@@ -81,8 +81,8 @@
<CardGrid>
<svelte:fragment slot="title">Delete organization</svelte:fragment>
The organization will be permanently deleted, including all projects and data associated
with this organization. This action is irreversible.
The organization will be permanently deleted, including all projects and data associated with
this organization. This action is irreversible.
<svelte:fragment slot="aside">
<BoxAvatar>
<svelte:fragment slot="image">
@@ -17,8 +17,8 @@
<CardGrid>
<svelte:fragment slot="title">DPA</svelte:fragment>
After downloading, have the DPA signed by your organization's compliance authority, such as your
CEO or Compliance Manager, and submit it to
After downloading, have the DPA signed by your organization's compliance authority, such as your CEO
or Compliance Manager, and submit it to
<a class="link" href="mailto:privacy@appwrite.io">privacy@appwrite.io</a>.
<svelte:fragment slot="aside">
<Box>
@@ -44,8 +44,8 @@
<Modal {error} onSubmit={update} bind:show on:close title={`${provider.name} OAuth2 settings`}>
<p slot="description">
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
To use {provider.name} authentication in your application, first fill in this form. For more info
you can
<Link.Anchor
class="link"
href={oAuthProvider?.docs}
@@ -45,8 +45,8 @@
<Modal {error} bind:show onSubmit={update} on:close title={`${provider.name} OAuth2 settings`}>
<svelte:fragment slot="description">
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
To use {provider.name} authentication in your application, first fill in this form. For more info
you can
<Link.Anchor href={oAuthProvider?.docs} target="_blank" rel="noopener noreferrer"
>visit the docs.</Link.Anchor>
</svelte:fragment>
@@ -46,8 +46,8 @@
<Modal {error} bind:show onSubmit={update} on:close title={`${provider.name} OAuth2 settings`}>
<p slot="description">
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
To use {provider.name} authentication in your application, first fill in this form. For more info
you can
<Link.Anchor
class="link"
href={oAuthProvider?.docs}
@@ -44,8 +44,8 @@
<Modal {error} bind:show onSubmit={update} on:close title={`${provider.name} OAuth2 settings`}>
<p slot="description">
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
To use {provider.name} authentication in your application, first fill in this form. For more info
you can
<Link.Anchor href={oAuthProvider?.docs} target="_blank" rel="noopener noreferrer"
>visit the docs.</Link.Anchor>
</p>
@@ -43,8 +43,8 @@
<svelte:fragment slot="title">{provider.name} OAuth2 settings</svelte:fragment>
<p slot="description">
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
To use {provider.name} authentication in your application, first fill in this form. For more info
you can
<a class="link" href={oAuthProvider?.docs} target="_blank" rel="noopener noreferrer"
>visit the docs.</a>
</p>
@@ -41,8 +41,8 @@
<Modal {error} bind:show onSubmit={update} on:close title={`${provider.name} OAuth2 settings`}>
<p slot="description">
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
To use {provider.name} authentication in your application, first fill in this form. For more info
you can
<Link.Anchor
class="link"
href={oAuthProvider?.docs}
@@ -45,8 +45,8 @@
<Modal {error} onSubmit={update} bind:show on:close title={`${provider.name} OAuth2 settings`}>
<p slot="description">
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
To use {provider.name} authentication in your application, first fill in this form. For more info
you can
<a class="link" href={oAuthProvider?.docs} target="_blank" rel="noopener noreferrer">
visit the docs.
</a>
@@ -67,8 +67,8 @@
<Modal {error} onSubmit={update} title="{provider.name} OAuth2 settings" bind:show on:close>
<p slot="description">
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
To use {provider.name} authentication in your application, first fill in this form. For more info
you can
<a class="link" href={oAuthProvider?.docs} target="_blank" rel="noopener noreferrer"
>visit the docs.</a>
</p>
@@ -55,8 +55,8 @@
on:close
title={`${provider.name} OAuth2 settings`}>
<p slot="description">
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
To use {provider.name} authentication in your application, first fill in this form. For more info
you can
<a class="link" href={oAuthProvider?.docs} target="_blank" rel="noopener noreferrer"
>visit the docs.</a>
</p>
@@ -43,8 +43,8 @@
More info
</Tag>
<p slot="tooltip">
User identities are the user's connected OAuth accounts. <br /> The user can
sign in using these identities.
User identities are the user's connected OAuth accounts. <br /> The user can sign
in using these identities.
</p>
</Popover>
</div>
@@ -25,8 +25,8 @@
</Tag>
<p slot="tooltip">
User targets include emails, phone numbers, and devices with your app
installed.<br /> These targets can subscribe to a topic and receive messages
published to it.
installed.<br /> These targets can subscribe to a topic and receive messages published
to it.
</p>
</Popover>
</div>
@@ -326,8 +326,8 @@ APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.project).c
<Icon icon={IconInfo} size="s" />
<span slot="tooltip">
The hostname that your website will use to interact with the
{resolvedProfile.platform} APIs in production or development
environments. No protocol or port number required.
{resolvedProfile.platform} APIs in production or development environments.
No protocol or port number required.
</span>
</Tooltip>
</InputText>
@@ -171,8 +171,8 @@
</Layout.Stack>
{:else if isSelfHosted && !isVcsEnabled}
<Alert.Inline status="info" title="Installing Git on a self-hosted instance">
Before installing Git in a locally hosted {resolvedProfile.platform} project, ensure
your environment variables are configured.
Before installing Git in a locally hosted {resolvedProfile.platform} project, ensure your
environment variables are configured.
<svelte:fragment slot="actions">
<FormButton
compact
@@ -1,7 +1,7 @@
<script lang="ts">
import { Container } from '$lib/layout';
import { CardGrid, Card, ProgressBarBig } from '$lib/components';
import { showUsageRatesModal, tierToPlan, upgradeURL } from '$lib/stores/billing';
import { plansInfo, showUsageRatesModal, 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';
@@ -9,34 +9,40 @@
import { formatNum } from '$lib/helpers/string';
import { total } from '$lib/layout/usage.svelte';
import { BillingPlan } from '$lib/constants.js';
import { base } from '$app/paths';
import { resolve } from '$app/paths';
import { formatCurrency, formatNumberWithCommas, clampMin } from '$lib/helpers/numbers';
import { getCountryName } from '$lib/helpers/diallingCodes.js';
import { Accordion, Icon, Layout, Link, Table, Typography } from '@appwrite.io/pink-svelte';
import { IconChartSquareBar } from '@appwrite.io/pink-icons-svelte';
import { page } from '$app/state';
import { isFreePlan, isPaidPlan } from '$lib/helpers/billing.js';
import { resolvedProfile } from '$lib/profiles/index.svelte';
import { Platform } from '@appwrite.io/console';
import { ProfileMode, resolvedProfile } from '$lib/profiles/index.svelte';
import type { PageProps } from './$types';
export let data;
const { data, params }: PageProps = $props();
$: baseRoute = `${base}/project-${page.params.region}-${page.params.project}`;
$: network = data.usage.network;
$: users = data.usage.users;
$: usersTotal = data.usage.usersTotal;
$: executions = data.usage.executions;
$: executionsTotal = data.usage.executionsTotal;
$: storage =
const baseRoute = $derived(
resolve('/(console)/project-[region]-[project]', {
region: params.region,
project: params.project
})
);
const network = $derived(data.usage.network);
const users = $derived(data.usage.users);
const usersTotal = $derived(data.usage.usersTotal);
const executions = $derived(data.usage.executions);
const executionsTotal = $derived(data.usage.executionsTotal);
const storage = $derived(
data.usage.filesStorageTotal +
data.usage.deploymentsStorageTotal +
data.usage.buildsStorageTotal;
$: imageTransformations = data.usage.imageTransformations;
$: imageTransformationsTotal = data.usage.imageTransformationsTotal;
$: dbReads = data.usage.databasesReads;
$: dbWrites = data.usage.databasesWrites;
data.usage.deploymentsStorageTotal +
data.usage.buildsStorageTotal
);
const imageTransformations = $derived(data.usage.imageTransformations);
const imageTransformationsTotal = $derived(data.usage.imageTransformationsTotal);
const dbReads = $derived(data.usage.databasesReads);
const dbWrites = $derived(data.usage.databasesWrites);
$: legendData = [
const legendData = $derived([
{
name: 'Reads',
value: clampMin(data.usage.databasesReads.reduce((sum, item) => sum + item.value, 0))
@@ -45,10 +51,10 @@
name: 'Writes',
value: clampMin(data.usage.databasesWrites.reduce((sum, item) => sum + item.value, 0))
}
];
]);
const tier = data?.currentInvoice?.plan ?? $organization?.billingPlan;
const plan = tierToPlan(tier).name;
const plan = $derived($plansInfo.get(data?.currentInvoice?.plan ?? $organization?.billingPlan));
const planName = $derived(plan.name);
</script>
<Container>
@@ -66,19 +72,19 @@
<p class="text">
On the Scale plan, you'll be charged only for any usage that exceeds the thresholds
per resource listed below. <Link.Button
on:click={() => ($showUsageRatesModal = true)}
onclick={() => ($showUsageRatesModal = true)}
>Learn more about plan usage limits.</Link.Button>
</p>
{:else if isPaidPlan($organization.billingPlan)}
<p class="text">
On the Pro plan, you'll be charged only for any usage that exceeds the thresholds
per resource listed below. <Link.Button
on:click={() => ($showUsageRatesModal = true)}
onclick={() => ($showUsageRatesModal = true)}
>Learn more about plan usage limits.</Link.Button>
</p>
{:else if isFreePlan($organization?.billingPlan)}
<p class="text">
If you exceed the limits of the {plan} plan, services for your projects may be disrupted.
If you exceed the limits of the {planName} plan, services for your projects may be disrupted.
<Link.Anchor href={$upgradeURL} class="link"
>Upgrade for greater capacity</Link.Anchor
>.
@@ -86,40 +92,54 @@
{/if}
</div>
{#if resolvedProfile.platform === Platform.Imagine}
{#if resolvedProfile.id === ProfileMode.STUDIO}
<CardGrid>
<svelte:fragment slot="title">Imagine Credits</svelte:fragment>
Total Imagine credits consumed across your project. Resets at the start of each billing cycle.
<svelte:fragment slot="title">Credits</svelte:fragment>
Current credit usage included in your plan.
<svelte:fragment slot="aside">
{#if data.usage.imagineCredits}
{@const creditsTotal = data.usage.imagineCredits.reduce(
(sum, item) => sum + item.value,
0
)}
<Layout.Stack gap="s" direction="row" alignItems="baseline">
<Typography.Title>
{formatNumberWithCommas(creditsTotal)}
</Typography.Title>
<Typography.Text>Credits</Typography.Text>
{@const creditsLimit = plan.limits?.credits ?? plan.limits?.dailyCredits ?? 0}
{@const percentageUsed =
creditsLimit > 0 ? Math.round((creditsTotal / creditsLimit) * 100) : 0}
<Layout.Stack direction="row" justifyContent="space-between">
<Layout.Stack gap="s" direction="row" alignItems="baseline" inline>
<Typography.Title>
{formatNumberWithCommas(creditsTotal)}
</Typography.Title>
<Typography.Text>Credits used</Typography.Text>
</Layout.Stack>
<Layout.Stack direction="row" gap="xs" alignItems="center" inline>
<Typography.Text>
{percentageUsed}%
</Typography.Text>
</Layout.Stack>
</Layout.Stack>
{#if data.usage.imagineCredits.length > 0}
<BarChart
options={{
yAxis: {
axisLabel: {
formatter: formatNum
}
<ProgressBarBig
progressValue={creditsTotal}
progressMax={creditsLimit}
progressBarData={[
{
size: creditsTotal,
color: 'var(--bgcolor-neutral-invert)',
tooltip: {
title: 'Credits',
label: formatNumberWithCommas(creditsTotal)
}
}}
series={[
{
name: 'Credits',
data: [
...data.usage.imagineCredits.map((e) => [e.date, e.value])
]
}
]} />
{/if}
}
]} />
<Layout.Stack direction="row" justifyContent="space-between">
<Typography.Text>{formatNumberWithCommas(creditsTotal)}</Typography.Text>
<Typography.Text>{formatNumberWithCommas(creditsLimit)}</Typography.Text>
</Layout.Stack>
{:else}
<Card isDashed>
<Layout.Stack gap="xs" alignItems="center" justifyContent="center">
@@ -184,8 +184,8 @@
<Form onSubmit={update}>
<CardGrid>
<svelte:fragment slot="title">Build settings</svelte:fragment>
Default build settings are configured based on your framework, ensuring optimal performance.
Adjust the settings here if needed.
Default build settings are configured based on your framework, ensuring optimal performance. Adjust
the settings here if needed.
<svelte:fragment slot="aside">
{@const adapterData = adapterDataList.find(
(adapterData) => adapterData.framework === frameworkKey.toLowerCase()
@@ -64,8 +64,8 @@
<Form onSubmit={updateRuntime}>
<CardGrid>
<svelte:fragment slot="title">Runtime settings</svelte:fragment>
Select the runtime for building and serving your site. Version changes apply on redeploy and
can be updated here.
Select the runtime for building and serving your site. Version changes apply on redeploy and can
be updated here.
<svelte:fragment slot="aside">
<InputSelect
label="Build runtime"