mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'billing' of github.com:appwrite/console into fix-billing-usage
This commit is contained in:
Generated
+390
-374
File diff suppressed because it is too large
Load Diff
@@ -115,7 +115,7 @@
|
||||
|
||||
<div class="main-header-end">
|
||||
<nav class="u-flex is-only-desktop u-cross-center">
|
||||
{#if isCloud && $organization?.billingPlan !== 'tier-2' && !$page.url.pathname.startsWith('/console/account')}
|
||||
{#if isCloud && $organization?.billingPlan === 'tier-0' && !$page.url.pathname.startsWith('/console/account')}
|
||||
<Button secondary on:click={() => wizard.start(ChangeOrganizationTierCloud)}>
|
||||
Upgrade
|
||||
</Button>
|
||||
|
||||
@@ -13,12 +13,10 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Container } from '$lib/layout';
|
||||
import { BarChart, LineChart } from '$lib/charts';
|
||||
import { BarChart } from '$lib/charts';
|
||||
import { Card, SecondaryTabs, SecondaryTabsItem, Heading } from '$lib/components';
|
||||
import { Colors } from '$lib/charts/config';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { page } from '$app/stores';
|
||||
import { Tiles } from '../components/index.js';
|
||||
|
||||
type MetricMetadata = {
|
||||
title: string;
|
||||
@@ -26,17 +24,9 @@
|
||||
};
|
||||
|
||||
export let title: string;
|
||||
export let total: number;
|
||||
export let count: Models.Metric[];
|
||||
export let created: Models.Metric[];
|
||||
export let read: Models.Metric[];
|
||||
export let updated: Models.Metric[];
|
||||
export let deleted: Models.Metric[];
|
||||
|
||||
export let countMetadata: MetricMetadata;
|
||||
export let createdMetadata: MetricMetadata;
|
||||
export let readMetadata: MetricMetadata;
|
||||
export let updatedMetadata: MetricMetadata;
|
||||
export let deletedMetadata: MetricMetadata;
|
||||
export let path: string = null;
|
||||
</script>
|
||||
|
||||
@@ -59,7 +49,7 @@
|
||||
</div>
|
||||
<Card>
|
||||
{#if count}
|
||||
<Heading tag="h6" size="6">{total(count)}</Heading>
|
||||
<Heading tag="h6" size="6">{total}</Heading>
|
||||
<p>{countMetadata.title}</p>
|
||||
<div class="u-margin-block-start-16" />
|
||||
<div class="chart-container">
|
||||
@@ -73,76 +63,6 @@
|
||||
</div>
|
||||
{/if}
|
||||
</Card>
|
||||
<Tiles>
|
||||
<Card isTile>
|
||||
{#if created}
|
||||
<Heading tag="h6" size="6">{total(created)}</Heading>
|
||||
<p>{createdMetadata.title}</p>
|
||||
<div class="u-margin-block-start-16" />
|
||||
<div class="chart-container">
|
||||
<LineChart
|
||||
series={[
|
||||
{
|
||||
name: createdMetadata.legend,
|
||||
data: [...created.map((e) => [e.date, e.value])],
|
||||
color: Colors.Secondary
|
||||
}
|
||||
]} />
|
||||
</div>
|
||||
{/if}
|
||||
</Card>
|
||||
<Card isTile>
|
||||
{#if read}
|
||||
<Heading tag="h6" size="6">{total(read)}</Heading>
|
||||
<p>{readMetadata.title}</p>
|
||||
<div class="u-margin-block-start-16" />
|
||||
<div class="chart-container">
|
||||
<LineChart
|
||||
series={[
|
||||
{
|
||||
name: readMetadata.legend,
|
||||
data: [...read.map((e) => [e.date, e.value])],
|
||||
color: Colors.Tertiary
|
||||
}
|
||||
]} />
|
||||
</div>
|
||||
{/if}
|
||||
</Card>
|
||||
<Card isTile>
|
||||
{#if updated}
|
||||
<Heading tag="h6" size="6">{total(updated)}</Heading>
|
||||
<p>{updatedMetadata.title}</p>
|
||||
<div class="u-margin-block-start-16" />
|
||||
<div class="chart-container">
|
||||
<LineChart
|
||||
series={[
|
||||
{
|
||||
name: updatedMetadata.legend,
|
||||
data: [...updated.map((e) => [e.date, e.value])],
|
||||
color: Colors.Quaternary
|
||||
}
|
||||
]} />
|
||||
</div>
|
||||
{/if}
|
||||
</Card>
|
||||
<Card isTile>
|
||||
{#if deleted}
|
||||
<Heading tag="h6" size="6">{total(deleted)}</Heading>
|
||||
<p>{deletedMetadata.title}</p>
|
||||
<div class="u-margin-block-start-16" />
|
||||
<div class="chart-container">
|
||||
<LineChart
|
||||
series={[
|
||||
{
|
||||
name: deletedMetadata.legend,
|
||||
data: [...deleted.map((e) => [e.date, e.value])],
|
||||
color: Colors.Quinary
|
||||
}
|
||||
]} />
|
||||
</div>
|
||||
{/if}
|
||||
</Card>
|
||||
</Tiles>
|
||||
</Container>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
+16
-1
@@ -282,6 +282,22 @@ export class Billing {
|
||||
);
|
||||
}
|
||||
|
||||
async deleteOrganization(organizationId: string): Promise<Organization> {
|
||||
const path = `/organizations/${organizationId}`;
|
||||
const params = {
|
||||
organizationId
|
||||
};
|
||||
const uri = new URL(this.client.config.endpoint + path);
|
||||
return await this.client.call(
|
||||
'DELETE',
|
||||
uri,
|
||||
{
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
async getPlan(organizationId: string): Promise<Plan> {
|
||||
const path = `/organizations/${organizationId}/plan`;
|
||||
const params = {
|
||||
@@ -487,7 +503,6 @@ export class Billing {
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: add date range
|
||||
async listUsage(
|
||||
organizationId: string,
|
||||
startDate: string = null,
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
export type Metric = {
|
||||
/**
|
||||
* The value of this metric at the timestamp.
|
||||
*/
|
||||
value: number;
|
||||
/**
|
||||
* The date at which this metric was aggregated in ISO 8601 format.
|
||||
*/
|
||||
date: string;
|
||||
};
|
||||
/**
|
||||
* UsageDatabases
|
||||
*/
|
||||
export type UsageDatabases = {
|
||||
/**
|
||||
* The time range of the usage stats.
|
||||
*/
|
||||
range: string;
|
||||
/**
|
||||
* Aggregated total statistics of documents.
|
||||
*/
|
||||
databasesTotal: number;
|
||||
/**
|
||||
* Aggregated total statistics of collections.
|
||||
*/
|
||||
collectionsTotal: number;
|
||||
/**
|
||||
* Aggregated total statistics of documents.
|
||||
*/
|
||||
documentsTotal: number;
|
||||
/**
|
||||
* Aggregated total statistics of documents per period.
|
||||
*/
|
||||
databases: Metric[];
|
||||
/**
|
||||
* Aggregated total statistics of collections per period.
|
||||
*/
|
||||
collections: Metric[];
|
||||
/**
|
||||
* Aggregated total statistics of documents per period.
|
||||
*/
|
||||
documents: Metric[];
|
||||
};
|
||||
/**
|
||||
* UsageDatabase
|
||||
*/
|
||||
export type UsageDatabase = {
|
||||
/**
|
||||
* The time range of the usage stats.
|
||||
*/
|
||||
range: string;
|
||||
/**
|
||||
* Aggregated total statistics of collections.
|
||||
*/
|
||||
collectionsTotal: number;
|
||||
/**
|
||||
* Aggregated total statistics of documents.
|
||||
*/
|
||||
documentsTotal: number;
|
||||
/**
|
||||
* Aggregated statistics collections per period.
|
||||
*/
|
||||
collections: Metric[];
|
||||
/**
|
||||
* Aggregated statistics of documents per period.
|
||||
*/
|
||||
documents: Metric[];
|
||||
};
|
||||
/**
|
||||
* UsageCollection
|
||||
*/
|
||||
export type UsageCollection = {
|
||||
/**
|
||||
* The time range of the usage stats.
|
||||
*/
|
||||
range: string;
|
||||
/**
|
||||
* Aggregated total statistics of documents.
|
||||
*/
|
||||
documentsTotal: number;
|
||||
/**
|
||||
* Aggregated statistics of documents per period.
|
||||
*/
|
||||
documents: Metric[];
|
||||
};
|
||||
/**
|
||||
* UsageUsers
|
||||
*/
|
||||
export type UsageUsers = {
|
||||
/**
|
||||
* The time range of the usage stats.
|
||||
*/
|
||||
range: string;
|
||||
/**
|
||||
* Aggregated total statistics of users.
|
||||
*/
|
||||
usersTotal: number;
|
||||
/**
|
||||
* Aggregated total statistics sessions created.
|
||||
*/
|
||||
sessionsTotal: number;
|
||||
/**
|
||||
* Aggregated statistics of users per period.
|
||||
*/
|
||||
users: Metric[];
|
||||
/**
|
||||
* Aggregated statistics sessions created per period.
|
||||
*/
|
||||
sessions: Metric[];
|
||||
};
|
||||
/**
|
||||
* StorageUsage
|
||||
*/
|
||||
export type UsageStorage = {
|
||||
/**
|
||||
* The time range of the usage stats.
|
||||
*/
|
||||
range: string;
|
||||
/**
|
||||
* Aggregated total statistics of buckets
|
||||
*/
|
||||
bucketsTotal: number;
|
||||
/**
|
||||
* Aggregated total statistics of files.
|
||||
*/
|
||||
filesTotal: number;
|
||||
/**
|
||||
* Aggregated total statistics of files storage (in bytes).
|
||||
*/
|
||||
filesStorageTotal: number;
|
||||
/**
|
||||
* Aggregated statistics of buckets per period.
|
||||
*/
|
||||
buckets: Metric[];
|
||||
/**
|
||||
* Aggregated statistics of files per period.
|
||||
*/
|
||||
files: Metric[];
|
||||
/**
|
||||
* Aggregated statistics of storage (in bytes) per period .
|
||||
*/
|
||||
storage: Metric[];
|
||||
};
|
||||
/**
|
||||
* UsageBuckets
|
||||
*/
|
||||
export type UsageBuckets = {
|
||||
/**
|
||||
* The time range of the usage stats.
|
||||
*/
|
||||
range: string;
|
||||
/**
|
||||
* Aggregated total statistics of bucket files.
|
||||
*/
|
||||
filesTotal: number;
|
||||
/**
|
||||
* Aggregated total statistics of bucket files storage.
|
||||
*/
|
||||
filesStorageTotal: number;
|
||||
/**
|
||||
* Aggregated statistics of bucket files per period.
|
||||
*/
|
||||
files: Metric[];
|
||||
/**
|
||||
* Aggregated statistics of bucket storage files per period.
|
||||
*/
|
||||
storage: Metric[];
|
||||
};
|
||||
/**
|
||||
* UsageFunctions
|
||||
*/
|
||||
export type UsageFunctions = {
|
||||
/**
|
||||
* The time range of the usage stats.
|
||||
*/
|
||||
range: string;
|
||||
/**
|
||||
* Aggregated total statistics of functions.
|
||||
*/
|
||||
functionsTotal: number;
|
||||
/**
|
||||
* Aggregated total statistics of function deployments.
|
||||
*/
|
||||
deploymentsTotal: number;
|
||||
/**
|
||||
* Aggregated total statistics of function deployments storage.
|
||||
*/
|
||||
deploymentsStorageTotal: number;
|
||||
/**
|
||||
* Aggregated total statistics of function builds.
|
||||
*/
|
||||
buildsTotal: number;
|
||||
/**
|
||||
* Aggregated total statistics of builds storage.
|
||||
*/
|
||||
buildsStorageTotal: number;
|
||||
/**
|
||||
* Aggregated total statistics of build compute time.
|
||||
*/
|
||||
buildsTimeTotal: number;
|
||||
/**
|
||||
* Aggregated total statistics of functions executions.
|
||||
*/
|
||||
executionsTotal: number;
|
||||
/**
|
||||
* Aggregated total statistics of functions execution compute time.
|
||||
*/
|
||||
executionsTimeTotal: number;
|
||||
/**
|
||||
* Aggregated statistics of functions per period.
|
||||
*/
|
||||
functions: Metric[];
|
||||
/**
|
||||
* Aggregated statistics of deployments per period.
|
||||
*/
|
||||
deployments: Metric[];
|
||||
/**
|
||||
* Aggregated statistics of deployments storage per period.
|
||||
*/
|
||||
deploymentsStorage: Metric[];
|
||||
/**
|
||||
* Aggregated statistics of builds per period.
|
||||
*/
|
||||
builds: Metric[];
|
||||
/**
|
||||
* Aggregated statistics of storage per period.
|
||||
*/
|
||||
buildsStorage: Metric[];
|
||||
/**
|
||||
* Aggregated statistics of builds compute time per period.
|
||||
*/
|
||||
buildsTime: Metric[];
|
||||
/**
|
||||
* Aggregated statistics of executions per period.
|
||||
*/
|
||||
executions: Metric[];
|
||||
/**
|
||||
* Aggregated statistics of execution compute time per period.
|
||||
*/
|
||||
executionsTime: Metric[];
|
||||
};
|
||||
/**
|
||||
* UsageProject
|
||||
*/
|
||||
export type UsageProject = {
|
||||
/**
|
||||
* The time range of the usage stats.
|
||||
*/
|
||||
range: number;
|
||||
/**
|
||||
* Aggregated statistics of number of requests per period.
|
||||
*/
|
||||
requests: string[];
|
||||
/**
|
||||
* Aggregated statistics of consumed bandwidth per period.
|
||||
*/
|
||||
network: string[];
|
||||
/**
|
||||
* Aggregated statistics of total function executions.
|
||||
*/
|
||||
executionsTotal: number;
|
||||
/**
|
||||
* Aggregated statistics of total number of documents.
|
||||
*/
|
||||
documentsTotal: number;
|
||||
/**
|
||||
* Aggregated statistics of total number of databases.
|
||||
*/
|
||||
databasesTotal: number;
|
||||
/**
|
||||
* Aggregated statistics of total number of users.
|
||||
*/
|
||||
usersTotal: number;
|
||||
/**
|
||||
* Aggregated statistics of total occupied storage size (in bytes).
|
||||
*/
|
||||
filesStorageTotal: number;
|
||||
/**
|
||||
* Aggregated statistics of total number of buckets.
|
||||
*/
|
||||
bucketsTotal: number;
|
||||
};
|
||||
@@ -89,25 +89,20 @@ export const failedInvoice = cachedStore<
|
||||
});
|
||||
|
||||
export type TierData = {
|
||||
price: number;
|
||||
name: string;
|
||||
description: string;
|
||||
collaboratorPrice?: number;
|
||||
};
|
||||
|
||||
export const tierFree: TierData = {
|
||||
price: 0,
|
||||
name: 'Starter',
|
||||
description: 'For personal, passion projects.'
|
||||
};
|
||||
|
||||
export const tierPro: TierData = {
|
||||
price: 15,
|
||||
name: 'Pro',
|
||||
description: 'For pro developers and production projects that need the ability to scale.'
|
||||
};
|
||||
export const tierScale: TierData = {
|
||||
price: 685,
|
||||
name: 'Scale',
|
||||
description: 'For scaling teams that need dedicated support.'
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ const TemplateRuntimes = {
|
||||
NODE: { name: 'node', versions: ['20.0', '19.0', '18.0', '16.0', '14.5'] },
|
||||
PHP: { name: 'php', versions: ['8.2', '8.1', '8.0'] },
|
||||
RUBY: { name: 'ruby', versions: ['3.2', '3.1', '3.0'] },
|
||||
PYTHON: { name: 'python', versions: ['3.11', '3.10', '3.9', '3.8'] },
|
||||
PYTHON: { name: 'python', versions: ['3.12', '3.11', '3.10', '3.9', '3.8'] },
|
||||
DART: { name: 'dart', versions: ['3.1', '3.0', '2.19', '2.18', '2.17', '2.16', '2.16'] },
|
||||
BUN: { name: 'bun', versions: ['1.0'] }
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ export type Organization = Models.Team<Record<string, unknown>> & {
|
||||
budgetAlerts: number[];
|
||||
paymentMethodId: string;
|
||||
backupPaymentMethodId: string;
|
||||
markedForDeletion: boolean;
|
||||
billingLimits: BillingLimits;
|
||||
billingCurrentInvoiceDate: string;
|
||||
billingNextInvoiceDate: string;
|
||||
|
||||
@@ -416,8 +416,7 @@
|
||||
</svelte:fragment>
|
||||
</HeaderAlert>
|
||||
{/if}
|
||||
<!-- TODO: add variable from backend that indicates org is due to be deleted -->
|
||||
{#if false}
|
||||
{#if $organization?.markedForDeletion && !$page.url.pathname.includes('/console/account')}
|
||||
<HeaderAlert title="Organization flagged for deletion">
|
||||
<svelte:fragment>
|
||||
All existing projects in the {$organization.name} organization have been paused.
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import Delete from './deleteOrganization.svelte';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
|
||||
export let data;
|
||||
let name: string;
|
||||
let showDelete = false;
|
||||
|
||||
@@ -89,4 +90,4 @@
|
||||
{/if}
|
||||
</Container>
|
||||
|
||||
<Delete bind:showDelete />
|
||||
<Delete bind:showDelete invoices={data.invoices} />
|
||||
|
||||
@@ -2,9 +2,11 @@ import type { PageLoad } from './$types';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
|
||||
export const load: PageLoad = async ({ depends }) => {
|
||||
export const load: PageLoad = async ({ depends, parent }) => {
|
||||
const { organization } = await parent();
|
||||
depends(Dependencies.ORGANIZATION);
|
||||
return {
|
||||
projects: await sdk.forConsole.projects.list()
|
||||
projects: await sdk.forConsole.projects.list(),
|
||||
invoices: await sdk.forConsole.billing.listInvoices(organization.$id)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -10,21 +10,26 @@
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { projects } from '../store';
|
||||
import { toLocaleDate } from '$lib/helpers/date';
|
||||
import { isCloud } from '$lib/system';
|
||||
import type { InvoiceList } from '$lib/sdk/billing';
|
||||
|
||||
export let showDelete = false;
|
||||
export let invoices: InvoiceList;
|
||||
let error: string = null;
|
||||
|
||||
//TODO: replace variable with value from method
|
||||
const TEMPORARY_SET_FOR_DELETION = true;
|
||||
|
||||
async function deleteOrg() {
|
||||
try {
|
||||
await sdk.forConsole.teams.delete($organization.$id);
|
||||
await invalidate(Dependencies.ACCOUNT);
|
||||
if (isCloud) {
|
||||
await sdk.forConsole.billing.deleteOrganization($organization.$id);
|
||||
} else {
|
||||
await sdk.forConsole.teams.delete($organization.$id);
|
||||
}
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `${$organization.name} has been deleted`
|
||||
});
|
||||
await invalidate(Dependencies.ACCOUNT);
|
||||
await invalidate(Dependencies.ORGANIZATION);
|
||||
trackEvent(Submit.OrganizationDelete);
|
||||
if ($organizationList?.total > 1) {
|
||||
goto(`${base}/console/`);
|
||||
@@ -37,6 +42,8 @@
|
||||
trackError(e, Submit.OrganizationDelete);
|
||||
}
|
||||
}
|
||||
|
||||
$: upcomingInvoice = invoices.invoices.find((i) => i.status === 'upcoming' && i.amount > 0);
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
@@ -47,7 +54,7 @@
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}>
|
||||
{#if TEMPORARY_SET_FOR_DELETION}
|
||||
{#if upcomingInvoice}
|
||||
<p data-private>
|
||||
The organization <b>{$organization.name}</b> will be flagged for deletion.
|
||||
</p>
|
||||
|
||||
@@ -96,10 +96,10 @@
|
||||
{user.emailVerification && user.phoneVerification
|
||||
? 'verified'
|
||||
: user.emailVerification
|
||||
? 'verified email'
|
||||
: user.phoneVerification
|
||||
? 'verified phone'
|
||||
: 'unverified'}
|
||||
? 'verified email'
|
||||
: user.phoneVerification
|
||||
? 'verified phone'
|
||||
: 'unverified'}
|
||||
</Pill>
|
||||
{:else}
|
||||
<Pill danger>blocked</Pill>
|
||||
|
||||
@@ -4,39 +4,16 @@
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
$: count = data.count;
|
||||
$: created = data.created;
|
||||
$: read = data.read;
|
||||
$: updated = data.updated;
|
||||
$: deleted = data.deleted;
|
||||
$: total = data.usersTotal;
|
||||
$: count = data.users;
|
||||
</script>
|
||||
|
||||
<Usage
|
||||
title="Users"
|
||||
path={`/console/project-${$page.params.project}/auth/usage`}
|
||||
{total}
|
||||
{count}
|
||||
{created}
|
||||
{read}
|
||||
{updated}
|
||||
{deleted}
|
||||
countMetadata={{
|
||||
legend: 'Users',
|
||||
title: 'Registered users'
|
||||
}}
|
||||
createdMetadata={{
|
||||
legend: 'Create',
|
||||
title: 'Users created'
|
||||
}}
|
||||
readMetadata={{
|
||||
legend: 'Read',
|
||||
title: 'Users read'
|
||||
}}
|
||||
updatedMetadata={{
|
||||
legend: 'Update',
|
||||
title: 'Users updated'
|
||||
}}
|
||||
deletedMetadata={{
|
||||
legend: 'Delete',
|
||||
title: 'Users deleted'
|
||||
}} />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import type { Metric, UsageUsers } from '$lib/sdk/usage';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
import { error } from '@sveltejs/kit';
|
||||
@@ -6,13 +6,12 @@ import { error } from '@sveltejs/kit';
|
||||
export const load: PageLoad = async ({ params }) => {
|
||||
const { period } = params;
|
||||
try {
|
||||
const response = await sdk.forProject.users.getUsage(period ?? '30d');
|
||||
const response = (await sdk.forProject.users.getUsage(
|
||||
period ?? '30d'
|
||||
)) as unknown as UsageUsers;
|
||||
return {
|
||||
count: response.usersCount as unknown as Models.Metric[],
|
||||
created: response.usersCreate as unknown as Models.Metric[],
|
||||
read: response.usersRead as unknown as Models.Metric[],
|
||||
updated: response.usersUpdate as unknown as Models.Metric[],
|
||||
deleted: response.usersDelete as unknown as Models.Metric[]
|
||||
usersTotal: response.usersTotal,
|
||||
users: response.users as Metric[]
|
||||
};
|
||||
} catch (e) {
|
||||
throw error(e.code, e.message);
|
||||
|
||||
@@ -112,18 +112,18 @@
|
||||
{$user.emailVerification && $user.phoneVerification
|
||||
? 'verified'
|
||||
: $user.emailVerification
|
||||
? 'verified email'
|
||||
: $user.phoneVerification
|
||||
? 'verified phone'
|
||||
: 'unverified'}
|
||||
? 'verified email'
|
||||
: $user.phoneVerification
|
||||
? 'verified phone'
|
||||
: 'unverified'}
|
||||
</Pill>
|
||||
{:else}
|
||||
<Pill success={$user.emailVerification || $user.phoneVerification}>
|
||||
{$user.emailVerification
|
||||
? 'verified '
|
||||
: $user.phoneVerification
|
||||
? 'verified '
|
||||
: 'unverified'}
|
||||
? 'verified '
|
||||
: 'unverified'}
|
||||
</Pill>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
+3
-26
@@ -4,39 +4,16 @@
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
$: count = data.count;
|
||||
$: created = data.created;
|
||||
$: read = data.read;
|
||||
$: updated = data.updated;
|
||||
$: deleted = data.deleted;
|
||||
$: total = data.documentsTotal;
|
||||
$: count = data.documents;
|
||||
</script>
|
||||
|
||||
<Usage
|
||||
title="Databases"
|
||||
path={`/console/project-${$page.params.project}/databases/database-${$page.params.database}/collection-${$page.params.collection}/usage`}
|
||||
{total}
|
||||
{count}
|
||||
{created}
|
||||
{read}
|
||||
{updated}
|
||||
{deleted}
|
||||
countMetadata={{
|
||||
legend: 'Documents',
|
||||
title: 'Total documents'
|
||||
}}
|
||||
createdMetadata={{
|
||||
legend: 'Create',
|
||||
title: 'Documents created'
|
||||
}}
|
||||
readMetadata={{
|
||||
legend: 'Read',
|
||||
title: 'Documents read'
|
||||
}}
|
||||
updatedMetadata={{
|
||||
legend: 'Update',
|
||||
title: 'Documents updated'
|
||||
}}
|
||||
deletedMetadata={{
|
||||
legend: 'Delete',
|
||||
title: 'Documents deleted'
|
||||
}} />
|
||||
|
||||
+5
-8
@@ -1,19 +1,16 @@
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import type { Metric, UsageCollection } from '$lib/sdk/usage';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ params }) => {
|
||||
const response = await sdk.forProject.databases.getCollectionUsage(
|
||||
const response = (await sdk.forProject.databases.getCollectionUsage(
|
||||
params.database,
|
||||
params.collection,
|
||||
params.period ?? '30d'
|
||||
);
|
||||
)) as unknown as UsageCollection;
|
||||
|
||||
return {
|
||||
count: response.documentsCount as unknown as Models.Metric[],
|
||||
created: response.documentsCreate as unknown as Models.Metric[],
|
||||
read: response.documentsRead as unknown as Models.Metric[],
|
||||
updated: response.documentsUpdate as unknown as Models.Metric[],
|
||||
deleted: response.documentsDelete as unknown as Models.Metric[]
|
||||
documentsTotal: response.documentsTotal,
|
||||
documents: response.documents as Metric[]
|
||||
};
|
||||
};
|
||||
|
||||
+3
-25
@@ -5,38 +5,16 @@
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
$: count = data.count;
|
||||
$: created = data.created;
|
||||
$: read = data.read;
|
||||
$: updated = data.updated;
|
||||
$: deleted = data.deleted;
|
||||
$: total = data.collectionsTotal;
|
||||
$: count = data.collections;
|
||||
</script>
|
||||
|
||||
<Usage
|
||||
title="Databases"
|
||||
path={`/console/project-${$page.params.project}/databases/database-${$page.params.database}/usage`}
|
||||
{total}
|
||||
{count}
|
||||
{created}
|
||||
{read}
|
||||
{updated}
|
||||
{deleted}
|
||||
countMetadata={{
|
||||
legend: 'Collections',
|
||||
title: 'Total collections'
|
||||
}}
|
||||
createdMetadata={{
|
||||
legend: 'Create',
|
||||
title: 'Collections created'
|
||||
}}
|
||||
readMetadata={{
|
||||
legend: 'Read',
|
||||
title: 'Collections read'
|
||||
}}
|
||||
updatedMetadata={{
|
||||
legend: 'Update',
|
||||
title: 'Collections updated'
|
||||
}}
|
||||
deletedMetadata={{
|
||||
legend: 'Delete',
|
||||
title: 'Collections deleted'
|
||||
}} />
|
||||
|
||||
+5
-8
@@ -1,18 +1,15 @@
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import type { Metric, UsageDatabase } from '$lib/sdk/usage';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ params }) => {
|
||||
const response = await sdk.forProject.databases.getDatabaseUsage(
|
||||
const response = (await sdk.forProject.databases.getDatabaseUsage(
|
||||
params.database,
|
||||
params.period ?? '30d'
|
||||
);
|
||||
)) as unknown as UsageDatabase;
|
||||
|
||||
return {
|
||||
count: response.collectionsCount as unknown as Models.Metric[],
|
||||
created: response.collectionsCreate as unknown as Models.Metric[],
|
||||
read: response.collectionsRead as unknown as Models.Metric[],
|
||||
updated: response.collectionsUpdate as unknown as Models.Metric[],
|
||||
deleted: response.collectionsDelete as unknown as Models.Metric[]
|
||||
collectionsTotal: response.collectionsTotal,
|
||||
collections: response.collections as Metric[]
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,39 +4,16 @@
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
$: count = data.count;
|
||||
$: created = data.created;
|
||||
$: read = data.read;
|
||||
$: updated = data.updated;
|
||||
$: deleted = data.deleted;
|
||||
$: total = data.databasesTotal;
|
||||
$: count = data.databases;
|
||||
</script>
|
||||
|
||||
<Usage
|
||||
title="Databases"
|
||||
path={`/console/project-${$page.params.project}/databases/usage`}
|
||||
{total}
|
||||
{count}
|
||||
{created}
|
||||
{read}
|
||||
{updated}
|
||||
{deleted}
|
||||
countMetadata={{
|
||||
legend: 'Databases',
|
||||
title: 'Total databases'
|
||||
}}
|
||||
createdMetadata={{
|
||||
legend: 'Create',
|
||||
title: 'Databases created'
|
||||
}}
|
||||
readMetadata={{
|
||||
legend: 'Read',
|
||||
title: 'Databases read'
|
||||
}}
|
||||
updatedMetadata={{
|
||||
legend: 'Update',
|
||||
title: 'Databases updated'
|
||||
}}
|
||||
deletedMetadata={{
|
||||
legend: 'Delete',
|
||||
title: 'Databases deleted'
|
||||
}} />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import type { Metric, UsageDatabases } from '$lib/sdk/usage';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
import { error } from '@sveltejs/kit';
|
||||
@@ -6,13 +6,12 @@ import { error } from '@sveltejs/kit';
|
||||
export const load: PageLoad = async ({ params }) => {
|
||||
const { period } = params;
|
||||
try {
|
||||
const response = await sdk.forProject.databases.getUsage(period ?? '30d');
|
||||
const response = (await sdk.forProject.databases.getUsage(
|
||||
period ?? '30d'
|
||||
)) as unknown as UsageDatabases;
|
||||
return {
|
||||
count: response.databasesCount as unknown as Models.Metric[],
|
||||
created: response.databasesCreate as unknown as Models.Metric[],
|
||||
read: response.databasesRead as unknown as Models.Metric[],
|
||||
updated: response.databasesUpdate as unknown as Models.Metric[],
|
||||
deleted: response.databasesDelete as unknown as Models.Metric[]
|
||||
databasesTotal: response.databasesTotal,
|
||||
databases: response.databases as Metric[]
|
||||
};
|
||||
} catch (e) {
|
||||
throw error(e.code, e.message);
|
||||
|
||||
+3
-19
@@ -1,15 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { Container } from '$lib/layout';
|
||||
import { Card, SecondaryTabsItem, SecondaryTabs, Heading } from '$lib/components';
|
||||
import { total } from '$lib/layout/usage.svelte';
|
||||
import { BarChart } from '$lib/charts';
|
||||
import { page } from '$app/stores';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
$: count = data.count;
|
||||
$: errors = data.errors;
|
||||
$: total = data.executionsTotal;
|
||||
$: count = data.executions;
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
@@ -35,7 +33,7 @@
|
||||
</div>
|
||||
{#if count}
|
||||
<Card>
|
||||
<Heading tag="h6" size="6">{total(count)}</Heading>
|
||||
<Heading tag="h6" size="6">{total}</Heading>
|
||||
<p>Executions</p>
|
||||
<div class="u-margin-block-start-16" />
|
||||
<BarChart
|
||||
@@ -47,18 +45,4 @@
|
||||
]} />
|
||||
</Card>
|
||||
{/if}
|
||||
{#if errors}
|
||||
<Card>
|
||||
<Heading tag="h6" size="6">{total(errors)}</Heading>
|
||||
<p>Errors</p>
|
||||
<div class="u-margin-block-start-16" />
|
||||
<BarChart
|
||||
series={[
|
||||
{
|
||||
name: 'Count of function errors over time',
|
||||
data: [...errors.map((e) => [e.date, e.value])]
|
||||
}
|
||||
]} />
|
||||
</Card>
|
||||
{/if}
|
||||
</Container>
|
||||
|
||||
+6
-5
@@ -1,17 +1,18 @@
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import type { Metric, UsageFunctions } from '$lib/sdk/usage';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
export const load: PageLoad = async ({ params }) => {
|
||||
try {
|
||||
const response = await sdk.forProject.functions.getFunctionUsage(
|
||||
const response = (await sdk.forProject.functions.getFunctionUsage(
|
||||
params.function,
|
||||
params.period ?? '30d'
|
||||
);
|
||||
)) as unknown as UsageFunctions;
|
||||
|
||||
return {
|
||||
count: response.executionsTotal as unknown as Models.Metric[],
|
||||
errors: response.executionsFailure as unknown as Models.Metric[]
|
||||
executionsTotal: response.executionsTotal,
|
||||
executions: response.executions as Metric[]
|
||||
};
|
||||
} catch (e) {
|
||||
throw error(e.code, e.message);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" context="module">
|
||||
export function totalMetrics(set: Array<unknown>): number {
|
||||
if (!set) return 0;
|
||||
return total((set as Models.Metric[]).map((c) => c.value));
|
||||
return total((set as Metric[]).map((c) => c.value));
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
import { Heading, Tab } from '$lib/components';
|
||||
import { humanFileSize } from '$lib/helpers/sizeConvertion';
|
||||
import { Container, type UsagePeriods } from '$lib/layout';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { onMount } from 'svelte';
|
||||
import { onboarding, project } from '../store';
|
||||
import Bandwith from './bandwith.svelte';
|
||||
@@ -25,6 +24,7 @@
|
||||
import { usage } from './store';
|
||||
import { formatNum } from '$lib/helpers/string';
|
||||
import { total } from '$lib/helpers/array';
|
||||
import type { Metric } from '$lib/sdk/usage';
|
||||
|
||||
$: projectId = $page.params.project;
|
||||
$: path = `/console/project-${projectId}/overview`;
|
||||
@@ -82,7 +82,7 @@
|
||||
<Onboard {projectId} />
|
||||
{:else}
|
||||
{#if $usage}
|
||||
{@const storage = humanFileSize(totalMetrics($usage.storage) ?? 0)}
|
||||
{@const storage = humanFileSize($usage.filesStorageTotal ?? 0)}
|
||||
<section class="common-section">
|
||||
<div class="grid-dashboard-1s-2m-6l">
|
||||
<div class="card is-2-columns-medium-screen is-3-columns-large-screen">
|
||||
@@ -106,14 +106,14 @@
|
||||
|
||||
<div class="grid-item-1-end-start">
|
||||
<div class="heading-level-4">
|
||||
{formatNum(totalMetrics($usage.documents) ?? 0)}
|
||||
{formatNum($usage.documentsTotal ?? 0)}
|
||||
</div>
|
||||
<div>Documents</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-end-end">
|
||||
<div class="text">
|
||||
Databases: {formatNum(totalMetrics($usage.databases) ?? 0)}
|
||||
Databases: {formatNum($usage.databasesTotal ?? 0)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -141,7 +141,7 @@
|
||||
|
||||
<div class="grid-item-1-end-end">
|
||||
<div class="text">
|
||||
Buckets: {formatNum(totalMetrics($usage.buckets) ?? 0)}
|
||||
Buckets: {formatNum($usage.bucketsTotal ?? 0)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -161,7 +161,7 @@
|
||||
|
||||
<div class="grid-item-1-end-start">
|
||||
<div class="heading-level-4">
|
||||
{formatNum(totalMetrics($usage.users) ?? 0)}
|
||||
{formatNum($usage.usersTotal ?? 0)}
|
||||
</div>
|
||||
<div>Users</div>
|
||||
</div>
|
||||
@@ -182,7 +182,7 @@
|
||||
|
||||
<div class="grid-item-1-end-start">
|
||||
<div class="heading-level-4">
|
||||
{formatNum(totalMetrics($usage.executions) ?? 0)}
|
||||
{formatNum($usage.executionsTotal ?? 0)}
|
||||
</div>
|
||||
<div>Executions</div>
|
||||
</div>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
date: number;
|
||||
value: number;
|
||||
}>;
|
||||
|
||||
$: bandwith = humanFileSize(totalMetrics($usage?.network));
|
||||
|
||||
$: if (period) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
export let indeterminate: boolean = false;
|
||||
export let checked: boolean = false;
|
||||
export let indeterminate = false;
|
||||
export let checked = false;
|
||||
|
||||
interface $$Props extends Partial<HTMLInputElement> {
|
||||
indeterminate?: boolean;
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { cachedStore } from '$lib/helpers/cache';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import type { UsageProject } from '$lib/sdk/usage';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
|
||||
export const usage = cachedStore<
|
||||
Models.UsageProject,
|
||||
UsageProject,
|
||||
{
|
||||
load: (range: string) => Promise<void>;
|
||||
}
|
||||
>('projectUsage', function ({ set }) {
|
||||
return {
|
||||
load: async (range) => {
|
||||
const usages = await sdk.forProject.project.getUsage(range);
|
||||
const usages: UsageProject = (await sdk.forProject.project.getUsage(
|
||||
range
|
||||
)) as unknown as UsageProject;
|
||||
set(usages);
|
||||
}
|
||||
};
|
||||
|
||||
+3
-26
@@ -4,39 +4,16 @@
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
$: count = data.count;
|
||||
$: created = data.created;
|
||||
$: read = data.read;
|
||||
$: updated = data.updated;
|
||||
$: deleted = data.deleted;
|
||||
$: total = data.filesTotal;
|
||||
$: count = data.files;
|
||||
</script>
|
||||
|
||||
<Usage
|
||||
title="Files"
|
||||
path={`/console/project-${$page.params.project}/storage/bucket-${$page.params.bucket}/usage`}
|
||||
{total}
|
||||
{count}
|
||||
{created}
|
||||
{read}
|
||||
{updated}
|
||||
{deleted}
|
||||
countMetadata={{
|
||||
legend: 'Files',
|
||||
title: 'Total files'
|
||||
}}
|
||||
createdMetadata={{
|
||||
legend: 'Create',
|
||||
title: 'Files created'
|
||||
}}
|
||||
readMetadata={{
|
||||
legend: 'Read',
|
||||
title: 'Files read'
|
||||
}}
|
||||
updatedMetadata={{
|
||||
legend: 'Update',
|
||||
title: 'Files updated'
|
||||
}}
|
||||
deletedMetadata={{
|
||||
legend: 'Delete',
|
||||
title: 'Files deleted'
|
||||
}} />
|
||||
|
||||
+5
-8
@@ -1,18 +1,15 @@
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import type { Metric, UsageBuckets } from '$lib/sdk/usage';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ params }) => {
|
||||
const response = await sdk.forProject.storage.getBucketUsage(
|
||||
const response = (await sdk.forProject.storage.getBucketUsage(
|
||||
params.bucket,
|
||||
params.period ?? '30d'
|
||||
);
|
||||
)) as unknown as UsageBuckets;
|
||||
|
||||
return {
|
||||
count: response.filesCount as unknown as Models.Metric[],
|
||||
created: response.filesCreate as unknown as Models.Metric[],
|
||||
read: response.filesRead as unknown as Models.Metric[],
|
||||
updated: response.filesUpdate as unknown as Models.Metric[],
|
||||
deleted: response.filesDelete as unknown as Models.Metric[]
|
||||
filesTotal: response.filesTotal,
|
||||
files: response.files as Metric[]
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,39 +4,16 @@
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
$: count = data.count;
|
||||
$: created = data.created;
|
||||
$: read = data.read;
|
||||
$: updated = data.updated;
|
||||
$: deleted = data.deleted;
|
||||
$: total = data.bucketsTotal;
|
||||
$: count = data.buckets;
|
||||
</script>
|
||||
|
||||
<Usage
|
||||
title="Buckets"
|
||||
path={`/console/project-${$page.params.project}/storage/usage`}
|
||||
{total}
|
||||
{count}
|
||||
{created}
|
||||
{read}
|
||||
{updated}
|
||||
{deleted}
|
||||
countMetadata={{
|
||||
legend: 'Buckets',
|
||||
title: 'Total buckets'
|
||||
}}
|
||||
createdMetadata={{
|
||||
legend: 'Create',
|
||||
title: 'Buckets created'
|
||||
}}
|
||||
readMetadata={{
|
||||
legend: 'Read',
|
||||
title: 'Buckets read'
|
||||
}}
|
||||
updatedMetadata={{
|
||||
legend: 'Update',
|
||||
title: 'Buckets updated'
|
||||
}}
|
||||
deletedMetadata={{
|
||||
legend: 'Delete',
|
||||
title: 'Buckets deleted'
|
||||
}} />
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import type { Metric, UsageStorage } from '$lib/sdk/usage';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { PageLoad } from './$types';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
export const load: PageLoad = async ({ params }) => {
|
||||
try {
|
||||
const response = await sdk.forProject.storage.getUsage(params.period ?? '30d');
|
||||
const response = (await sdk.forProject.storage.getUsage(
|
||||
params.period ?? '30d'
|
||||
)) as unknown as UsageStorage;
|
||||
|
||||
return {
|
||||
count: response.bucketsCount as unknown as Models.Metric[],
|
||||
created: response.bucketsCreate as unknown as Models.Metric[],
|
||||
read: response.bucketsRead as unknown as Models.Metric[],
|
||||
updated: response.bucketsUpdate as unknown as Models.Metric[],
|
||||
deleted: response.bucketsDelete as unknown as Models.Metric[]
|
||||
bucketsTotal: response.bucketsTotal,
|
||||
buckets: response.buckets as Metric[]
|
||||
};
|
||||
} catch (e) {
|
||||
throw error(e.code, e.message);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { VARS } from '$lib/system';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
|
||||
onDestroy(() => {
|
||||
$supportData = {
|
||||
@@ -37,7 +38,13 @@
|
||||
firstName: $user?.name ?? '',
|
||||
message: $supportData.message,
|
||||
tags: ['cloud'],
|
||||
customFields: [{ id: '41612', value: $supportData.category }]
|
||||
customFields: [
|
||||
{ id: '41612', value: $supportData.category },
|
||||
{ id: '48493', value: $user?.name ?? '' },
|
||||
{ id: '48492', value: $organization.$id ?? '' },
|
||||
// { id: '48491', value: $user?.projectId ?? '' },
|
||||
{ id: '48490', value: $user?.$id ?? '' }
|
||||
]
|
||||
})
|
||||
});
|
||||
trackEvent(Submit.SupportTicket);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Pill } from '$lib/elements';
|
||||
import { InputText, FormList } from '$lib/elements/forms';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { tierFree, tierPro, tierScale } from '$lib/stores/billing';
|
||||
import { plansInfo, tierFree, tierPro, tierScale } from '$lib/stores/billing';
|
||||
import { organizationList, type Organization } from '$lib/stores/organization';
|
||||
import { updateStepStatus } from '$lib/stores/wizard';
|
||||
import { createOrganization, createOrgSteps } from './store';
|
||||
@@ -28,6 +28,10 @@
|
||||
$createOrgSteps = updateStepStatus($createOrgSteps, 3, false);
|
||||
$createOrgSteps = updateStepStatus($createOrgSteps, 4, false);
|
||||
}
|
||||
|
||||
$: freePlan = $plansInfo.plans.find((p) => p.$id === 'tier-0');
|
||||
$: proPlan = $plansInfo.plans.find((p) => p.$id === 'tier-1');
|
||||
$: scalePlan = $plansInfo.plans.find((p) => p.$id === 'tier-2');
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
@@ -81,7 +85,7 @@
|
||||
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} - ${tierFree.price}/month
|
||||
{tierFree.name} - ${freePlan?.price}/month
|
||||
</h4>
|
||||
<p class="u-color-text-gray u-small">{tierFree.description}</p>
|
||||
</div>
|
||||
@@ -94,7 +98,7 @@
|
||||
<svelte:fragment slot="custom">
|
||||
<div class="u-flex u-flex-vertical u-gap-4 u-width-full-line">
|
||||
<h4 class="body-text-2 u-bold">
|
||||
{tierPro.name} - ${tierPro.price}/month per organization member + exta
|
||||
{tierPro.name} - ${proPlan.price}/month per organization member + exta
|
||||
usage
|
||||
</h4>
|
||||
<p class="u-color-text-gray u-small">
|
||||
@@ -116,7 +120,7 @@
|
||||
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} - ${tierScale.price}/month + extra usage
|
||||
{tierScale.name} - ${scalePlan.price}/month + extra usage
|
||||
</h4>
|
||||
<p class="u-color-text-gray u-small">
|
||||
{tierScale.description}
|
||||
|
||||
@@ -86,6 +86,10 @@
|
||||
$changeOrganizationTier.billingPlan = 'tier-1';
|
||||
}
|
||||
});
|
||||
|
||||
$: freePlan = $plansInfo.plans.find((p) => p.$id === 'tier-0');
|
||||
$: proPlan = $plansInfo.plans.find((p) => p.$id === 'tier-1');
|
||||
$: scalePlan = $plansInfo.plans.find((p) => p.$id === 'tier-2');
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
@@ -114,7 +118,7 @@
|
||||
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} - ${tierFree.price}/month
|
||||
{tierFree.name} - ${freePlan.price}/month
|
||||
</h4>
|
||||
<p class="u-color-text-gray u-small">{tierFree.description}</p>
|
||||
</div>
|
||||
@@ -139,7 +143,7 @@
|
||||
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} - ${tierPro.price}/month per organization member + extra
|
||||
{tierPro.name} - ${proPlan.price}/month per organization member + extra
|
||||
usage
|
||||
</h4>
|
||||
<p class="u-color-text-gray u-small">
|
||||
@@ -167,7 +171,7 @@
|
||||
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} - ${tierScale.price}/month + extra usage
|
||||
{tierScale.name} - ${scalePlan.price}/month + extra usage
|
||||
</h4>
|
||||
<p class="u-color-text-gray u-small">
|
||||
{tierScale.description}
|
||||
|
||||
@@ -167,11 +167,21 @@
|
||||
<p class="text">Additional members ({collaboratorsNumber})</p>
|
||||
<p class="text">${collaboratorPrice * collaboratorsNumber}</p>
|
||||
</span>
|
||||
{#if showCoupon && couponData?.status !== 'error'}
|
||||
<span class="u-flex u-main-space-between">
|
||||
<p class="text">Credits applied ({couponData.credits})</p>
|
||||
<p class="text">-${couponData.credits}</p>
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
||||
<div class="u-sep-block-start" />
|
||||
<span class="u-flex u-main-space-between">
|
||||
<p class="text">Estimated total</p>
|
||||
<p class="text">${totalExpences}</p>
|
||||
<p class="text">Estimated total (in USD)</p>
|
||||
<p class="text">
|
||||
${showCoupon && couponData?.status !== 'error'
|
||||
? totalExpences - couponData.credits || 0
|
||||
: totalExpences}
|
||||
</p>
|
||||
</span>
|
||||
|
||||
<p class="text u-margin-block-start-16">
|
||||
|
||||
Reference in New Issue
Block a user