feat: project usage

This commit is contained in:
Torsten Dittmann
2023-12-11 18:15:34 +01:00
parent fc12f4ec0b
commit be8b6c1dbc
15 changed files with 401 additions and 261 deletions
+4 -4
View File
@@ -6,7 +6,7 @@
"": {
"name": "@appwrite/console",
"dependencies": {
"@appwrite.io/console": "^0.3.0",
"@appwrite.io/console": "^0.4.1",
"@appwrite.io/pink": "0.2.0",
"@appwrite.io/pink-icons": "0.2.0",
"@popperjs/core": "^2.11.8",
@@ -159,9 +159,9 @@
"integrity": "sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg=="
},
"node_modules/@appwrite.io/console": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@appwrite.io/console/-/console-0.3.0.tgz",
"integrity": "sha512-XHKQHirzQliXoDpMLakSdXwpq9aPoeTABNhW8Z7MK3p6oBfp2gMplDD43Fbd91JREXoJ+RG8okX0z0nIc6hDiQ==",
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@appwrite.io/console/-/console-0.4.1.tgz",
"integrity": "sha512-GxT6bz1ct0dpvvp6xjVSp/VVUCGxaEAG11vi2KTWjJvRlITwHLYNXt5iycx+pe65gc2FNGTsApcCr1d6sRqLsQ==",
"dependencies": {
"cross-fetch": "3.1.5",
"isomorphic-form-data": "2.0.0"
+1 -1
View File
@@ -18,7 +18,7 @@
"e2e": "playwright test tests/e2e"
},
"dependencies": {
"@appwrite.io/console": "^0.3.0",
"@appwrite.io/console": "^0.4.1",
"@appwrite.io/pink": "0.2.0",
"@appwrite.io/pink-icons": "0.2.0",
"@popperjs/core": "^2.11.8",
+1 -1
View File
@@ -7,7 +7,7 @@
export let progressMax: number;
export let showBar = true;
$: progress = Math.round((progressValue / progressMax) * 100);
$: progress = Math.min(Math.max(Math.round((progressValue / progressMax) * 100), 0), 100);
</script>
<section class="progress-bar">
+1
View File
@@ -1,5 +1,6 @@
export function getProjectId() {
const pathname = window.location.pathname + '/';
const projectMatch = pathname.match(/\/project-(.*?)\//);
return projectMatch?.[1] || null;
}
+26 -1
View File
@@ -1,13 +1,38 @@
<script context="module" lang="ts">
export type UsagePeriods = '24h' | '30d' | '90d';
export function periodToDates(period: UsagePeriods): {
start: string;
end: string;
period: '1h' | '1d';
} {
const end = new Date();
switch (period) {
case '24h':
end.setHours(end.getHours() + 24);
break;
case '30d':
end.setDate(end.getDate() + 30);
break;
case '90d':
end.setDate(end.getDate() + 90);
break;
}
return {
start: new Date().toISOString(),
end: end.toISOString(),
period: period === '24h' ? '1h' : '1d'
};
}
export function last(set: Models.Metric[]): Models.Metric | null {
if (!set) return null;
return set.slice(-1)[0] ?? null;
}
export function total(set: Models.Metric[]): number {
return set.reduce((prev, curr) => prev + curr.value, 0);
return set?.reduce((prev, curr) => prev + curr.value, 0) ?? 0;
}
</script>
+11 -5
View File
@@ -512,15 +512,21 @@ export class Billing {
async listUsage(
organizationId: string,
startDate: string = null,
endDate: string = null
startDate: string = undefined,
endDate: string = undefined
): Promise<OrganizationUsage> {
const path = `/organizations/${organizationId}/usage`;
const params = {
organizationId,
startDate,
endDate
organizationId
};
if (startDate !== undefined) {
params['startDate'] = startDate;
}
if (endDate !== undefined) {
params['endDate'] = endDate;
}
const uri = new URL(this.client.config.endpoint + path);
return await this.client.call(
'get',
@@ -9,10 +9,11 @@
import { goto } from '$app/navigation';
import { toLocaleDate } from '$lib/helpers/date.js';
import { bytesToSize, humanFileSize } from '$lib/helpers/sizeConvertion';
import { abbreviateNumber } from '$lib/helpers/numbers';
import { BarChart } from '$lib/charts';
import ChangeOrganizationTierCloud from '$routes/console/changeOrganizationTierCloud.svelte';
import ProjectBreakdown from './ProjectBreakdown.svelte';
import { last } from '$lib/helpers/array';
import { formatNum } from '$lib/helpers/string';
export let data;
@@ -94,7 +95,7 @@
</p>
<svelte:fragment slot="aside">
{@const current = data.organizationUsage.bandwidth[0]?.value ?? 0}
{@const current = last(data.organizationUsage.bandwidth)?.value ?? 0}
{@const currentHumanized = humanFileSize(current)}
{@const max = getServiceLimit('bandwidth', tier)}
<ProgressBarBig
@@ -106,10 +107,24 @@
progressMax={max}
showBar={false} />
<BarChart
options={{
yAxis: {
axisLabel: {
formatter: (value) =>
value
? `${humanFileSize(+value).value} ${humanFileSize(+value).unit}`
: '0'
}
}
}}
series={[
{
name: 'Bandwidth',
data: [...data.organizationUsage.bandwidth.map((e) => [e.date, e.value])]
data: [...data.organizationUsage.bandwidth.map((e) => [e.date, e.value])],
tooltip: {
valueFormatter: (value) =>
`${humanFileSize(+value).value} ${humanFileSize(+value).unit}`
}
}
]} />
<ProjectBreakdown
@@ -125,17 +140,24 @@
<p class="text">The total number of users across all projects in your organization.</p>
<svelte:fragment slot="aside">
{@const current = data.organizationUsage.users[0]?.value ?? 0}
{@const current = last(data.organizationUsage.users)?.value ?? 0}
{@const max = getServiceLimit('users', tier)}
<ProgressBarBig
currentUnit="Users"
currentValue={current.toString()}
currentValue={formatNum(current)}
maxUnit="Users"
maxValue={abbreviateNumber(max)}
maxValue={formatNum(max)}
progressValue={current}
progressMax={max}
showBar={false} />
<BarChart
options={{
yAxis: {
axisLabel: {
formatter: formatNum
}
}
}}
series={[
{
name: 'Users',
@@ -160,7 +182,7 @@
currentUnit="Executions"
currentValue={current.toString()}
maxUnit="Executions"
maxValue={abbreviateNumber(max)}
maxValue={formatNum(max)}
progressValue={current}
progressMax={max} />
<ProjectBreakdown
@@ -1,31 +1,26 @@
import { sdk } from '$lib/stores/sdk';
import { Query } from '@appwrite.io/console';
import type { PageLoad } from './$types';
import type { Organization } from '$lib/stores/organization';
import type { Invoice } from '$lib/sdk/billing';
import { getTomorrow } from '$lib/helpers/date';
import { Query } from '@appwrite.io/console';
export const load: PageLoad = async ({ params, parent }) => {
const { invoice } = params;
const parentData = await parent();
const org = parentData.organization as Organization;
const tomorrow = getTomorrow(new Date());
let startDate: string;
let endDate: string;
let currentInvoice: Invoice | null = null;
let startDate: string = undefined;
let endDate: string = undefined;
let currentInvoice: Invoice = undefined;
if (invoice) {
currentInvoice = await sdk.forConsole.billing.getInvoice(org.$id, invoice);
startDate = currentInvoice.from;
endDate = currentInvoice.to;
} else {
startDate = org.billingCurrentInvoiceDate;
endDate = tomorrow.toISOString();
}
const [invoices, usage] = await Promise.all([
sdk.forConsole.billing.listInvoices(org.$id),
sdk.forConsole.billing.listInvoices(org.$id, [Query.orderDesc('from')]),
sdk.forConsole.billing.listUsage(params.organization, startDate, endDate)
]);
@@ -54,8 +54,8 @@
<Collapsible>
<CollapsibleItem>
<svelte:fragment slot="tgit restore --stageditle">Project breakdown</svelte:fragment>
<div class="table-wrapper">
<svelte:fragment slot="title">Project breakdown</svelte:fragment>
<div class="table-wrapper" data-sveltekit-preload-data="off">
<Table noMargin noStyles>
<TableHeader>
<TableCellHead width={285}>Project</TableCellHead>
@@ -71,8 +71,9 @@
<TableCell title="Usage">{format(project.usage)}</TableCell>
<TableCellLink
title="Go to project usage"
href={getProjectUsageLink(project.projectId)}
>View project usage</TableCellLink>
href={getProjectUsageLink(project.projectId)}>
View project usage
</TableCellLink>
</TableRow>
{/each}
</TableBody>
@@ -25,6 +25,7 @@
import { formatNum } from '$lib/helpers/string';
import { total } from '$lib/helpers/array';
import type { Metric } from '$lib/sdk/usage';
import { periodToDates } from '$lib/layout/usage.svelte';
$: projectId = $page.params.project;
$: path = `/console/project-${projectId}/overview`;
@@ -34,7 +35,7 @@
afterNavigate(handle);
async function handle() {
const promise = usage.load(period);
const promise = changePeriod(period);
if ($usage) {
await promise;
@@ -43,7 +44,8 @@
function changePeriod(newPeriod: UsagePeriods) {
period = newPeriod;
usage.load(period);
const dates = periodToDates(newPeriod);
return usage.load(dates.start, dates.end, dates.period);
}
$: $registerCommands([
@@ -1,19 +1,17 @@
import { sdk } from '$lib/stores/sdk';
import { cachedStore } from '$lib/helpers/cache';
import type { UsageProject } from '$lib/sdk/usage';
import { writable, type Writable } from 'svelte/store';
import type { Models } from '@appwrite.io/console';
export const usage = cachedStore<
UsageProject,
Models.UsageProject,
{
load: (range: string) => Promise<void>;
load: (start: string, end: string, period: '1h' | '1d') => Promise<void>;
}
>('projectUsage', function ({ set }) {
return {
load: async (range) => {
const usages: UsageProject = (await sdk.forProject.project.getUsage(
range
)) as unknown as UsageProject;
load: async (start, end, period) => {
const usages = await sdk.forProject.project.getUsage(start, end, period);
set(usages);
}
};
@@ -0,0 +1,274 @@
<script lang="ts">
import type { Models } from '@appwrite.io/console';
import { page } from '$app/stores';
import { goto } from '$app/navigation';
import { Container } from '$lib/layout';
import { CardGrid, Heading, Card } from '$lib/components';
import {
TableBody,
TableCell,
TableCellLink,
TableCellHead,
TableHeader,
TableRow,
Table
} from '$lib/elements/table';
import { showUsageRatesModal, tierToPlan } from '$lib/stores/billing';
import { wizard } from '$lib/stores/wizard';
import { organization } from '$lib/stores/organization';
import { Button, InputSelect } from '$lib/elements/forms';
import { toLocaleDate } from '$lib/helpers/date';
import { humanFileSize } from '$lib/helpers/sizeConvertion';
import { BarChart } from '$lib/charts';
import { formatNum } from '$lib/helpers/string';
import { total } from '$lib/layout/usage.svelte';
import ChangeOrganizationTierCloud from '$routes/console/changeOrganizationTierCloud.svelte';
export let data;
$: base = `/console/project-${data.project.$id}`;
$: network = data.usage.network as unknown as Models.Metric[];
$: users = data.usage.users;
$: usersTotal = data.usage.usersTotal;
$: executions = data.usage.executionsTotal;
$: storage = data.usage.filesStorageTotal;
const tier = data?.currentInvoice?.tier ?? $organization?.billingPlan;
const plan = tierToPlan(tier).name;
let invoice = null;
async function handlePeriodChange() {
const target = invoice ? `${base}/settings/usage/${invoice}` : `${base}/settings/usage`;
if ($page.url.pathname !== target) {
await goto(target);
}
}
const cycles = data.invoices.invoices.map((invoice) => ({
label: toLocaleDate(invoice.from),
value: invoice.$id
}));
</script>
<Container>
<div class="u-flex u-cross-center u-main-space-between">
<Heading tag="h2" size="5">Usage</Heading>
{#if $organization?.billingPlan === 'tier-0'}
<Button on:click={() => wizard.start(ChangeOrganizationTierCloud)}>
<span class="text">Upgrade</span>
</Button>
{/if}
</div>
<div class="u-flex u-main-space-between common-section u-cross-center">
{#if $organization.billingPlan === 'tier-2'}
<p class="text">
On the Scale plan, you'll be charged only for any usage that exceeds the thresholds
per resource listed below. <button
on:click={() => ($showUsageRatesModal = true)}
class="link"
type="button">Learn more about plan usage limits.</button>
</p>
{:else}
<p class="text">
If you exceed the limits of the {plan} plan, services for your projects may be disrupted.
<button
on:click={() => wizard.start(ChangeOrganizationTierCloud)}
class="link"
type="button">Upgrade for greater capacity</button
>.
</p>
{/if}
<div class="u-flex u-gap-8 u-cross-center">
<p class="text">Usage period:</p>
<InputSelect
wrapperTag="div"
id="period"
label="Usage period"
showLabel={false}
bind:value={invoice}
on:change={handlePeriodChange}
options={[
{
label: 'Current billing cycle',
value: null
},
...cycles
]} />
</div>
</div>
<CardGrid>
<Heading tag="h4" size="7">Bandwidth</Heading>
<p class="text">Calculated for all bandwidth used in your project.</p>
<svelte:fragment slot="aside">
{#if network}
{@const humanized = humanFileSize(total(network))}
<div class="u-flex u-gap-8 u-cross-baseline">
<Heading tag="h5" size="4">{humanized.value}</Heading>
<Heading tag="h6" size="6">{humanized.unit}</Heading>
</div>
<BarChart
options={{
yAxis: {
axisLabel: {
formatter: (value) =>
value
? `${humanFileSize(+value).value} ${
humanFileSize(+value).unit
}`
: '0'
}
}
}}
series={[
{
name: 'Bandwidth',
data: [...network.map((e) => [e.date, e.value])],
tooltip: {
valueFormatter: (value) =>
`${humanFileSize(+value).value} ${humanFileSize(+value).unit}`
}
}
]} />
{:else}
<Card isDashed>
<div class="u-flex u-cross-center u-flex-vertical u-main-center u-flex">
<span
class="icon-chart-square-bar text-large"
aria-hidden="true"
style="font-size: 32px;" />
<p class="u-bold">No data to show</p>
</div>
</Card>
{/if}
</svelte:fragment>
</CardGrid>
<CardGrid>
<Heading tag="h6" size="7">Users</Heading>
<p class="text">Total user in your project.</p>
<svelte:fragment slot="aside">
{@const current = formatNum(usersTotal)}
<div class="u-flex u-flex-vertical">
<div class="u-flex u-main-space-between">
<p>
<span class="heading-level-4">{current}</span>
<span class="body-text-1 u-bold">Users</span>
</p>
</div>
</div>
<BarChart
options={{
yAxis: {
axisLabel: {
formatter: formatNum
}
}
}}
series={[
{
name: 'Users',
data: [...users.map((e) => [e.date, e.value])]
}
]} />
</svelte:fragment>
</CardGrid>
<CardGrid>
<Heading tag="h6" size="7">Function executions</Heading>
<p class="text">
Calculated for all functions that are executed in all projects in your project.
</p>
<svelte:fragment slot="aside">
{@const current = formatNum(executions)}
<div class="u-flex u-flex-vertical">
<div class="u-flex u-main-space-between">
<p>
<span class="heading-level-4">{current}</span>
<span class="body-text-1 u-bold">Executions</span>
</p>
</div>
</div>
{#if data.usage.executionsBreakdown.length > 0}
<Table noMargin noStyles>
<TableHeader>
<TableCellHead width={285}>Project</TableCellHead>
<TableCellHead>Usage</TableCellHead>
<TableCellHead width={140} />
</TableHeader>
<TableBody>
{#each data.usage.executionsBreakdown as func}
<TableRow>
<TableCell title="Function">
{func.name ?? func.resourceId}
</TableCell>
<TableCell title="Usage">
{formatNum(func.value)} executions
</TableCell>
<TableCellLink
href={`${base}/functions/function-${func.resourceId}`}
title="View function">
View function
</TableCellLink>
</TableRow>
{/each}
</TableBody>
</Table>
{/if}
</svelte:fragment>
</CardGrid>
<CardGrid>
<Heading tag="h6" size="7">Storage</Heading>
<p class="text">
Calculated for all your files, deployments, builds and databases. While in beta, only
file storage is counted against your plan limits.
</p>
<svelte:fragment slot="aside">
{@const humanized = humanFileSize(storage)}
<div class="u-flex u-flex-vertical">
<div class="u-flex u-main-space-between">
<p>
<span class="heading-level-4">{humanized.value}</span>
<span class="body-text-1 u-bold">{humanized.unit}</span>
</p>
</div>
</div>
{#if data.usage.bucketsBreakdown.length > 0}
<Table noMargin noStyles>
<TableHeader>
<TableCellHead width={285}>Bucket</TableCellHead>
<TableCellHead>Usage</TableCellHead>
<TableCellHead width={140} />
</TableHeader>
<TableBody>
{#each data.usage.bucketsBreakdown.sort((a, b) => b.value - a.value) as bucket}
{@const humanized = humanFileSize(bucket.value)}
<TableRow>
<TableCell title="Function">
{bucket.name ?? bucket.resourceId}
</TableCell>
<TableCell title="Usage">
{humanized.value}{humanized.unit}
</TableCell>
<TableCellLink
href={`${base}/storage/bucket-${bucket.resourceId}`}
title="View bucket">
View bucket
</TableCellLink>
</TableRow>
{/each}
</TableBody>
</Table>
{/if}
</svelte:fragment>
</CardGrid>
<p class="text u-text-color-gray u-margin-block-start-16">
Metrics are estimates updated every 24 hours and may not accurately reflect your invoice.
</p>
</Container>
@@ -0,0 +1,35 @@
import { getSdkForProject, sdk } from '$lib/stores/sdk';
import { Query } from '@appwrite.io/console';
import type { PageLoad } from './$types';
import type { Organization } from '$lib/stores/organization';
import type { Invoice } from '$lib/sdk/billing';
export const load: PageLoad = async ({ params, parent }) => {
const { invoice, project } = params;
const parentData = await parent();
const org = parentData.organization as Organization;
let startDate: string = org.billingCurrentInvoiceDate;
let endDate: string = org.billingNextInvoiceDate;
let currentInvoice: Invoice = undefined;
if (invoice) {
currentInvoice = await sdk.forConsole.billing.getInvoice(org.$id, invoice);
startDate = currentInvoice.from;
endDate = currentInvoice.to;
}
const [invoices, usage] = await Promise.all([
sdk.forConsole.billing.listInvoices(org.$id, [Query.orderDesc('from')]),
/**
* Workaround because project id might not be populated yet.
*/
getSdkForProject(project).project.getUsage(startDate, endDate)
]);
return {
usage,
invoices,
currentInvoice
};
};
@@ -1,195 +0,0 @@
<script lang="ts">
import { Container } from '$lib/layout';
import { Card, SecondaryTabsItem, SecondaryTabs, Heading, CardGrid } from '$lib/components';
import { total } from '$lib/layout/usage.svelte';
import { BarChart } from '$lib/charts';
import { page } from '$app/stores';
import { bytesToSize, humanFileSize } from '$lib/helpers/sizeConvertion';
import { toDecimals } from '$lib/helpers/numbers.js';
import { showUsageRatesModal, tierToPlan } from '$lib/stores/billing.js';
import { organization } from '$lib/stores/organization.js';
import { isCloud } from '$lib/system';
export let data;
$: files = data.filesTotal ? normalizeFileSize(data.filesTotal) : null;
$: executions = data.executionsTotal;
$: users = data.usersTotal;
//All file sizes get converted to the bigger file size unit
function normalizeFileSize(
files: {
date: string;
value: number;
}[]
) {
console.log(data.filesTotal);
const sizes = files?.map((e) => e.value);
const biggestSize = Math.max(...sizes);
const unit = humanFileSize(biggestSize).unit;
return {
data: files?.map((e) => ({
date: new Date(e.date).toLocaleDateString(),
value: toDecimals(bytesToSize(e.value, unit), 2)
})),
unit
};
}
</script>
<Container>
<div class="u-flex u-main-space-between common-section u-cross-center">
<Heading tag="h2" size="5">Usage</Heading>
<SecondaryTabs>
<SecondaryTabsItem
href={`/console/project-${data.project.$id}/settings/usage/24h`}
disabled={($page.params.period ?? '24h') === '24h'}>
24h
</SecondaryTabsItem>
<SecondaryTabsItem
href={`/console/project-${data.project.$id}/settings/usage/30d`}
disabled={$page.params.period === '30d'}>
30d
</SecondaryTabsItem>
<SecondaryTabsItem
href={`/console/project-${data.project.$id}/settings/usage/90d`}
disabled={$page.params.period === '90d'}>
90d
</SecondaryTabsItem>
</SecondaryTabs>
</div>
{#if isCloud}
<p class="u-margin-block-start-8">
Project resources contribute to your organization's monthly free usage limits on the {tierToPlan(
$organization.billingPlan
).name} plan.
<button
class="link"
type="button"
on:click|preventDefault={() => ($showUsageRatesModal = true)}
>Learn more about usage rates.</button>
</p>
{/if}
<CardGrid>
<Heading tag="h4" size="7">Bandwidth</Heading>
<p class="text">Calculated for all bandwidth used across your project.</p>
<svelte:fragment slot="aside">
{#if data?.requestsTotal && total(data.requestsTotal)}
<div class="u-flex u-gap-8 u-cross-baseline">
<Heading tag="h5" size="4">{total(data.requestsTotal)}</Heading>
<Heading tag="h6" size="6">GB</Heading>
</div>
<BarChart
series={[
{
name: 'Bandwidth over time',
data: [...data.requestsTotal.map((e) => [e.date, e.value])]
}
]} />
{:else}
<Card isDashed>
<div class="u-flex u-cross-center u-flex-vertical u-main-center u-flex">
<span
class="icon-chart-square-bar text-large"
aria-hidden="true"
style="font-size: 32px;" />
<p class="u-bold">No data to show</p>
</div>
</Card>
{/if}
</svelte:fragment>
</CardGrid>
<CardGrid>
<Heading tag="h4" size="7">Users</Heading>
<p class="text">The total number of users of your project.</p>
<svelte:fragment slot="aside">
{#if total(users)}
<div class="u-flex u-gap-8 u-cross-baseline">
<Heading tag="h5" size="4">{total(users)}</Heading>
<Heading tag="h6" size="6">Users</Heading>
</div>
<BarChart
series={[
{
name: 'Users over time',
data: [...users.map((e) => [e.date, e.value])]
}
]} />
{:else}
<Card isDashed>
<div class="u-flex u-cross-center u-flex-vertical u-main-center u-flex">
<span
class="icon-chart-square-bar text-large"
aria-hidden="true"
style="font-size: 32px;" />
<p class="u-bold">No data to show</p>
</div>
</Card>
{/if}
</svelte:fragment>
</CardGrid>
<CardGrid>
<Heading tag="h4" size="7">Function executions</Heading>
<p class="text">Calculated for all functions executed in your project.</p>
<svelte:fragment slot="aside">
{#if total(executions)}
<div class="u-flex u-gap-8 u-cross-baseline">
<Heading tag="h5" size="4">{total(executions)}</Heading>
<Heading tag="h6" size="6">Executions</Heading>
</div>
<BarChart
series={[
{
name: 'Executions over time',
data: [...executions.map((e) => [e.date, e.value])]
}
]} />
{:else}
<Card isDashed>
<div class="u-flex u-cross-center u-flex-vertical u-main-center u-flex">
<span
class="icon-chart-square-bar text-large"
aria-hidden="true"
style="font-size: 32px;" />
<p class="u-bold">No data to show</p>
</div>
</Card>
{/if}
</svelte:fragment>
</CardGrid>
{#if data.filesTotal}
<CardGrid>
<Heading tag="h4" size="7">Storage</Heading>
<p class="text">Calculated for all storage operations in your project.</p>
<svelte:fragment slot="aside">
{@const size = humanFileSize(total(data.filesTotal))}
{#if size}
<div class="u-flex u-gap-8 u-cross-baseline">
<Heading tag="h5" size="4">{size.value}</Heading>
<Heading tag="h6" size="6">{size.unit}</Heading>
</div>
<BarChart
series={[
{
name: `Storage over time in ${files.unit}`,
data: [...files.data]
}
]} />
{:else}
<Card isDashed>
<div class="u-flex u-cross-center u-flex-vertical u-main-center u-flex">
<span
class="icon-chart-square-bar text-large"
aria-hidden="true"
style="font-size: 32px;" />
<p class="u-bold">No data to show</p>
</div>
</Card>
{/if}
</svelte:fragment>
</CardGrid>
{/if}
<p class="text u-text-color-gray u-margin-block-start-16">
Metrics are estimates updated every 24 hours and may not accurately reflect your invoice.
</p>
</Container>
@@ -1,24 +0,0 @@
import type { Models } from '@appwrite.io/console';
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.project.getUsage(params.period ?? '30d');
console.log(response);
return {
range: response.range,
bucketsTotal: response.bucketsTotal as unknown as Models.Metric[],
filesTotal: response.filesStorage as unknown as Models.Metric[],
databasesTotal: response.databasesTotal as unknown as Models.Metric[],
documentsTotal: response.documentsTotal as unknown as Models.Metric[],
executionsTotal: response.executionsTotal as unknown as Models.Metric[],
networkTotal: response.network as unknown as Models.Metric[],
requestsTotal: response.requestsTotal as unknown as Models.Metric[],
usersTotal: response.usersTotal as unknown as Models.Metric[]
};
} catch (e) {
throw error(e.code, e.message);
}
};