mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
fix: usage
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
<main class="grid-1-1 is-full-page" id="main">
|
||||
<section class="u-flex u-flex-vertical side-bg">
|
||||
<div class="logo u-flex u-gap-16 u-margin-inline-auto is-not-mobile">
|
||||
<a href={user ? '/console' : '/'}>
|
||||
<a href={$user ? '/console' : '/'}>
|
||||
{#if $app.themeInUse === 'dark'}
|
||||
<img
|
||||
src={AppwriteLogoDark}
|
||||
|
||||
+17
-17
@@ -38,6 +38,23 @@
|
||||
export function total(set: Models.Metric[]): number {
|
||||
return set?.reduce((prev, curr) => prev + curr.value, 0) ?? 0;
|
||||
}
|
||||
|
||||
export function accumulateFromEndingTotal(
|
||||
metrics: Models.Metric[],
|
||||
endingTotal: number
|
||||
): Array<[string, number]> {
|
||||
return metrics.reduceRight(
|
||||
(acc, curr) => {
|
||||
acc.total -= curr.value;
|
||||
acc.data.unshift([curr.date, acc.total]);
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
total: endingTotal,
|
||||
data: []
|
||||
}
|
||||
).data;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -58,23 +75,6 @@
|
||||
export let count: Models.Metric[];
|
||||
export let countMetadata: MetricMetadata;
|
||||
export let path: string = null;
|
||||
|
||||
function accumulateFromEndingTotal(
|
||||
metrics: Models.Metric[],
|
||||
endingTotal: number
|
||||
): Array<[string, number]> {
|
||||
return metrics.reduceRight(
|
||||
(acc, curr) => {
|
||||
acc.total -= curr.value;
|
||||
acc.data.unshift([curr.date, acc.total]);
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
total: endingTotal,
|
||||
data: []
|
||||
}
|
||||
).data;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Client, Query } from '@appwrite.io/console';
|
||||
import type { Client, Models, Query } from '@appwrite.io/console';
|
||||
import type { Organization } from '../stores/organization';
|
||||
import type { PaymentMethod } from '@stripe/stripe-js';
|
||||
import type { Tier } from '$lib/stores/billing';
|
||||
@@ -155,15 +155,13 @@ export type Aggregation = {
|
||||
*/
|
||||
resources: OrganizationUsage;
|
||||
};
|
||||
type UsageMetric = {
|
||||
date: string;
|
||||
value: number;
|
||||
};
|
||||
|
||||
export type OrganizationUsage = {
|
||||
bandwidth: Array<UsageMetric>;
|
||||
executions: number;
|
||||
storage: number;
|
||||
users: Array<UsageMetric>;
|
||||
bandwidth: Array<Models.Metric>;
|
||||
executionsTotal: number;
|
||||
storageTotal: number;
|
||||
users: Array<Models.Metric>;
|
||||
usersTotal: number;
|
||||
projects: Array<{
|
||||
projectId: string;
|
||||
storage: number;
|
||||
|
||||
@@ -37,13 +37,13 @@
|
||||
excess = {
|
||||
bandwidth: totBandwidth > plan.bandwidth ? totBandwidth - plan.bandwidth : 0,
|
||||
storage:
|
||||
usage?.storage > sizeToBytes(plan.storage, 'GB')
|
||||
? usage.storage - sizeToBytes(plan.storage, 'GB')
|
||||
usage?.storageTotal > sizeToBytes(plan.storage, 'GB')
|
||||
? usage.storageTotal - sizeToBytes(plan.storage, 'GB')
|
||||
: 0,
|
||||
users: totUsers > (plan.users || Infinity) ? totUsers - plan.users : 0,
|
||||
executions:
|
||||
usage?.executions > sizeToBytes(plan.executions, 'GB')
|
||||
? usage.executions - sizeToBytes(plan.executions, 'GB')
|
||||
usage?.executionsTotal > sizeToBytes(plan.executions, 'GB')
|
||||
? usage.executionsTotal - sizeToBytes(plan.executions, 'GB')
|
||||
: 0,
|
||||
members:
|
||||
members?.total > (plan.members || Infinity)
|
||||
|
||||
@@ -1,41 +1,36 @@
|
||||
<script lang="ts">
|
||||
import { Container } from '$lib/layout';
|
||||
import { CardGrid, Heading, ProgressBarBig } from '$lib/components';
|
||||
import { Card, CardGrid, Heading, ProgressBarBig } from '$lib/components';
|
||||
import { getServiceLimit, 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 { page } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { toLocaleDate } from '$lib/helpers/date.js';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { bytesToSize, humanFileSize } from '$lib/helpers/sizeConvertion';
|
||||
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';
|
||||
import { total } from '$lib/layout/usage.svelte';
|
||||
import { accumulateFromEndingTotal, total } from '$lib/layout/usage.svelte';
|
||||
|
||||
export let data;
|
||||
|
||||
const tier = data?.currentInvoice?.tier ?? $organization?.billingPlan;
|
||||
const plan = tierToPlan(tier).name;
|
||||
|
||||
let invoice = null;
|
||||
// let invoice = null;
|
||||
// async function handlePeriodChange() {
|
||||
// const target = invoice
|
||||
// ? `/console/organization-${$organization.$id}/usage/${invoice}`
|
||||
// : `/console/organization-${$organization.$id}/usage`;
|
||||
// if ($page.url.pathname !== target) {
|
||||
// await goto(target);
|
||||
// }
|
||||
// }
|
||||
|
||||
async function handlePeriodChange() {
|
||||
const target = invoice
|
||||
? `/console/organization-${$organization.$id}/usage/${invoice}`
|
||||
: `/console/organization-${$organization.$id}/usage`;
|
||||
if ($page.url.pathname !== target) {
|
||||
await goto(target);
|
||||
}
|
||||
}
|
||||
|
||||
const cycles = data.invoices.invoices.map((invoice) => ({
|
||||
label: toLocaleDate(invoice.from),
|
||||
value: invoice.$id
|
||||
}));
|
||||
// const cycles = data.invoices.invoices.map((invoice) => ({
|
||||
// label: toLocaleDate(invoice.from),
|
||||
// value: invoice.$id
|
||||
// }));
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
@@ -77,7 +72,7 @@
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<div class="u-flex u-gap-8 u-cross-center u-hide">
|
||||
<!--<div class="u-flex u-gap-8 u-cross-center">
|
||||
<p class="text">Usage period:</p>
|
||||
<InputSelect
|
||||
wrapperTag="div"
|
||||
@@ -93,7 +88,7 @@
|
||||
},
|
||||
...cycles
|
||||
]} />
|
||||
</div>
|
||||
</div>-->
|
||||
</div>
|
||||
|
||||
<CardGrid>
|
||||
@@ -104,42 +99,58 @@
|
||||
</p>
|
||||
|
||||
<svelte:fragment slot="aside">
|
||||
{@const current = total(data.organizationUsage.bandwidth)}
|
||||
{@const currentHumanized = humanFileSize(current)}
|
||||
{@const max = getServiceLimit('bandwidth', tier)}
|
||||
<ProgressBarBig
|
||||
currentUnit={currentHumanized.unit}
|
||||
currentValue={currentHumanized.value}
|
||||
maxUnit="GB"
|
||||
maxValue={max.toString()}
|
||||
progressValue={bytesToSize(current, 'GB')}
|
||||
progressMax={max}
|
||||
showBar={false} />
|
||||
<BarChart
|
||||
options={{
|
||||
yAxis: {
|
||||
axisLabel: {
|
||||
formatter: (value) =>
|
||||
value
|
||||
? `${humanFileSize(+value).value} ${humanFileSize(+value).unit}`
|
||||
: '0'
|
||||
{#if data.organizationUsage.bandwidth}
|
||||
{@const current = total(data.organizationUsage.bandwidth)}
|
||||
{@const currentHumanized = humanFileSize(current)}
|
||||
{@const max = getServiceLimit('bandwidth', tier)}
|
||||
<ProgressBarBig
|
||||
currentUnit={currentHumanized.unit}
|
||||
currentValue={currentHumanized.value}
|
||||
maxUnit="GB"
|
||||
maxValue={max.toString()}
|
||||
progressValue={bytesToSize(current, 'GB')}
|
||||
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])],
|
||||
tooltip: {
|
||||
valueFormatter: (value) =>
|
||||
`${humanFileSize(+value).value} ${humanFileSize(+value).unit}`
|
||||
}}
|
||||
series={[
|
||||
{
|
||||
name: 'Bandwidth',
|
||||
data: [
|
||||
...data.organizationUsage.bandwidth.map((e) => [e.date, e.value])
|
||||
],
|
||||
tooltip: {
|
||||
valueFormatter: (value) =>
|
||||
`${humanFileSize(+value).value} ${humanFileSize(+value).unit}`
|
||||
}
|
||||
}
|
||||
}
|
||||
]} />
|
||||
<ProjectBreakdown
|
||||
projects={data.organizationUsage.projects}
|
||||
metric="bandwidth"
|
||||
{data} />
|
||||
]} />
|
||||
<ProjectBreakdown
|
||||
projects={data.organizationUsage.projects}
|
||||
metric="bandwidth"
|
||||
{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>
|
||||
|
||||
@@ -149,31 +160,49 @@
|
||||
<p class="text">The total number of users across all projects in your organization.</p>
|
||||
|
||||
<svelte:fragment slot="aside">
|
||||
{@const current = last(data.organizationUsage.users)?.value ?? 0}
|
||||
{@const max = getServiceLimit('users', tier)}
|
||||
<ProgressBarBig
|
||||
currentUnit="Users"
|
||||
currentValue={formatNum(current)}
|
||||
maxUnit="Users"
|
||||
maxValue={formatNum(max)}
|
||||
progressValue={current}
|
||||
progressMax={max}
|
||||
showBar={false} />
|
||||
<BarChart
|
||||
options={{
|
||||
yAxis: {
|
||||
axisLabel: {
|
||||
formatter: formatNum
|
||||
{#if data.organizationUsage.users}
|
||||
{@const current = data.organizationUsage.usersTotal}
|
||||
{@const max = getServiceLimit('users', tier)}
|
||||
<ProgressBarBig
|
||||
currentUnit="Users"
|
||||
currentValue={formatNum(current)}
|
||||
maxUnit="Users"
|
||||
maxValue={formatNum(max)}
|
||||
progressValue={current}
|
||||
progressMax={max}
|
||||
showBar={false} />
|
||||
<BarChart
|
||||
options={{
|
||||
yAxis: {
|
||||
axisLabel: {
|
||||
formatter: formatNum
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
series={[
|
||||
{
|
||||
name: 'Users',
|
||||
data: [...data.organizationUsage.users.map((e) => [e.date, e.value])]
|
||||
}
|
||||
]} />
|
||||
<ProjectBreakdown projects={data.organizationUsage.projects} metric="users" {data} />
|
||||
}}
|
||||
series={[
|
||||
{
|
||||
name: 'Users',
|
||||
data: accumulateFromEndingTotal(
|
||||
data.organizationUsage.users,
|
||||
data.organizationUsage.usersTotal
|
||||
)
|
||||
}
|
||||
]} />
|
||||
<ProjectBreakdown
|
||||
projects={data.organizationUsage.projects}
|
||||
metric="users"
|
||||
{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>
|
||||
|
||||
@@ -185,19 +214,31 @@
|
||||
</p>
|
||||
|
||||
<svelte:fragment slot="aside">
|
||||
{@const current = data.organizationUsage.executions}
|
||||
{@const max = getServiceLimit('executions', tier)}
|
||||
<ProgressBarBig
|
||||
currentUnit="Executions"
|
||||
currentValue={current.toString()}
|
||||
maxUnit="Executions"
|
||||
maxValue={formatNum(max)}
|
||||
progressValue={current}
|
||||
progressMax={max} />
|
||||
<ProjectBreakdown
|
||||
projects={data.organizationUsage.projects}
|
||||
metric="executions"
|
||||
{data} />
|
||||
{#if data.organizationUsage.executionsTotal}
|
||||
{@const current = data.organizationUsage.executionsTotal}
|
||||
{@const max = getServiceLimit('executions', tier)}
|
||||
<ProgressBarBig
|
||||
currentUnit="Executions"
|
||||
currentValue={current.toString()}
|
||||
maxUnit="Executions"
|
||||
maxValue={formatNum(max)}
|
||||
progressValue={current}
|
||||
progressMax={max} />
|
||||
<ProjectBreakdown
|
||||
projects={data.organizationUsage.projects}
|
||||
metric="executions"
|
||||
{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>
|
||||
|
||||
@@ -210,17 +251,32 @@
|
||||
</p>
|
||||
|
||||
<svelte:fragment slot="aside">
|
||||
{@const current = data.organizationUsage.storage}
|
||||
{@const currentHumanized = humanFileSize(current)}
|
||||
{@const max = getServiceLimit('storage', tier)}
|
||||
<ProgressBarBig
|
||||
currentUnit={currentHumanized.unit}
|
||||
currentValue={currentHumanized.value}
|
||||
maxUnit="GB"
|
||||
maxValue={max.toString()}
|
||||
progressValue={bytesToSize(current, 'GB')}
|
||||
progressMax={max} />
|
||||
<ProjectBreakdown projects={data.organizationUsage.projects} metric="storage" {data} />
|
||||
{#if data.organizationUsage.storageTotal}
|
||||
{@const current = data.organizationUsage.storageTotal}
|
||||
{@const currentHumanized = humanFileSize(current)}
|
||||
{@const max = getServiceLimit('storage', tier)}
|
||||
<ProgressBarBig
|
||||
currentUnit={currentHumanized.unit}
|
||||
currentValue={currentHumanized.value}
|
||||
maxUnit="GB"
|
||||
maxValue={max.toString()}
|
||||
progressValue={bytesToSize(current, 'GB')}
|
||||
progressMax={max} />
|
||||
<ProjectBreakdown
|
||||
projects={data.organizationUsage.projects}
|
||||
metric="storage"
|
||||
{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>
|
||||
|
||||
|
||||
@@ -9,8 +9,23 @@ export const load: PageLoad = async ({ params, parent }) => {
|
||||
const parentData = await parent();
|
||||
const org = parentData.organization as Organization;
|
||||
|
||||
let startDate: string = undefined;
|
||||
let endDate: string = undefined;
|
||||
/**
|
||||
* Temporary fix during migration to billing system
|
||||
*/
|
||||
if (org.billingCurrentInvoiceDate === null || org.billingNextInvoiceDate === null) {
|
||||
return {
|
||||
organizationUsage: {
|
||||
bandwidth: null,
|
||||
users: null,
|
||||
usersTotal: null,
|
||||
storageTotal: null,
|
||||
executionsTotal: null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let startDate: string = org.billingCurrentInvoiceDate;
|
||||
let endDate: string = org.billingNextInvoiceDate;
|
||||
let currentInvoice: Invoice = undefined;
|
||||
|
||||
if (invoice) {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { Container } from '$lib/layout';
|
||||
import { CardGrid, Heading, Card } from '$lib/components';
|
||||
import {
|
||||
@@ -15,8 +13,7 @@
|
||||
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 { Button } from '$lib/elements/forms';
|
||||
import { humanFileSize } from '$lib/helpers/sizeConvertion';
|
||||
import { BarChart } from '$lib/charts';
|
||||
import { formatNum } from '$lib/helpers/string';
|
||||
@@ -36,19 +33,18 @@
|
||||
const tier = data?.currentInvoice?.tier ?? $organization?.billingPlan;
|
||||
const plan = tierToPlan(tier).name;
|
||||
|
||||
let invoice = null;
|
||||
// let invoice = null;
|
||||
// async function handlePeriodChange() {
|
||||
// const target = invoice ? `${base}/settings/usage/${invoice}` : `${base}/settings/usage`;
|
||||
// if ($page.url.pathname !== target) {
|
||||
// await goto(target);
|
||||
// }
|
||||
// }
|
||||
|
||||
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
|
||||
}));
|
||||
// const cycles = data.invoices.invoices.map((invoice) => ({
|
||||
// label: toLocaleDate(invoice.from),
|
||||
// value: invoice.$id
|
||||
// }));
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
@@ -87,7 +83,7 @@
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<div class="u-flex u-gap-8 u-cross-center u-hide">
|
||||
<!--<div class="u-flex u-gap-8 u-cross-center">
|
||||
<p class="text">Usage period:</p>
|
||||
<InputSelect
|
||||
wrapperTag="div"
|
||||
@@ -103,7 +99,7 @@
|
||||
},
|
||||
...cycles
|
||||
]} />
|
||||
</div>
|
||||
</div>-->
|
||||
</div>
|
||||
<CardGrid>
|
||||
<Heading tag="h4" size="7">Bandwidth</Heading>
|
||||
@@ -157,29 +153,41 @@
|
||||
<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>
|
||||
{#if users}
|
||||
{@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>
|
||||
</div>
|
||||
<BarChart
|
||||
options={{
|
||||
yAxis: {
|
||||
axisLabel: {
|
||||
formatter: formatNum
|
||||
<BarChart
|
||||
options={{
|
||||
yAxis: {
|
||||
axisLabel: {
|
||||
formatter: formatNum
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
series={[
|
||||
{
|
||||
name: 'Users',
|
||||
data: [...users.map((e) => [e.date, e.value])]
|
||||
}
|
||||
]} />
|
||||
}}
|
||||
series={[
|
||||
{
|
||||
name: 'Users',
|
||||
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>
|
||||
@@ -190,54 +198,66 @@
|
||||
</p>
|
||||
|
||||
<svelte:fragment slot="aside">
|
||||
{@const current = formatNum(executionsTotal)}
|
||||
<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>
|
||||
{#if executions}
|
||||
{@const current = formatNum(executionsTotal)}
|
||||
<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>
|
||||
</div>
|
||||
<BarChart
|
||||
options={{
|
||||
yAxis: {
|
||||
axisLabel: {
|
||||
formatter: formatNum
|
||||
<BarChart
|
||||
options={{
|
||||
yAxis: {
|
||||
axisLabel: {
|
||||
formatter: formatNum
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
series={[
|
||||
{
|
||||
name: 'Executions',
|
||||
data: [...executions.map((e) => [e.date, e.value])]
|
||||
}
|
||||
]} />
|
||||
{#if data.usage.executionsBreakdown.length > 0}
|
||||
<Table noMargin noStyles>
|
||||
<TableHeader>
|
||||
<TableCellHead width={285}>Function</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>
|
||||
}}
|
||||
series={[
|
||||
{
|
||||
name: 'Executions',
|
||||
data: [...executions.map((e) => [e.date, e.value])]
|
||||
}
|
||||
]} />
|
||||
{#if data.usage.executionsBreakdown.length > 0}
|
||||
<Table noMargin noStyles>
|
||||
<TableHeader>
|
||||
<TableCellHead width={285}>Function</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}
|
||||
{: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>
|
||||
@@ -250,41 +270,53 @@
|
||||
</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>
|
||||
{#if storage}
|
||||
{@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>
|
||||
</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 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}
|
||||
{: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>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
const projectId = $page.params.project;
|
||||
const bucketId = $page.params.bucket;
|
||||
const usedStorage = bytesToSize(data.organizationUsage.storage, 'MB');
|
||||
const usedStorage = bytesToSize(data.organizationUsage.storageTotal, 'MB');
|
||||
const getPreview = (fileId: string) =>
|
||||
sdk.forProject.storage.getFilePreview(bucketId, fileId, 32, 32).toString() + '&mode=admin';
|
||||
|
||||
|
||||
@@ -46,12 +46,14 @@
|
||||
$changeOrganizationTier.limitOverflow = {
|
||||
bandwidth: totBandwidth > plan.bandwidth ? totBandwidth - plan.bandwidth : 0,
|
||||
storage:
|
||||
usage.storage[0] > sizeToBytes(plan.storage, 'GB')
|
||||
? usage.storage[0] - plan.storage
|
||||
usage.storageTotal > sizeToBytes(plan.storage, 'GB')
|
||||
? usage.storageTotal - plan.storage
|
||||
: 0,
|
||||
users: totUsers > plan.users ? totUsers - plan.users : 0,
|
||||
executions:
|
||||
usage.executions[0] > plan.executions ? usage.executions[0] - plan.executions : 0,
|
||||
usage.executionsTotal > plan.executions
|
||||
? usage.executionsTotal - plan.executions
|
||||
: 0,
|
||||
members: members.total > plan.members ? members.total - (plan.members || Infinity) : 0
|
||||
};
|
||||
if (
|
||||
|
||||
Reference in New Issue
Block a user