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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions src/lib/components/progressBarBig.svelte
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
<script lang="ts">
import { abbreviateNumber } from '$lib/helpers/numbers';
export let currentValue: string;
export let currentUnit: string;
export let maxValue: string;
export let maxUnit: string;
export let progressValue: number;
export let progressMax: number;
export let showBar = true;

export let unit: string;
export let used: number;
export let max: number;
export let status: null | 'warning' | 'error' = null;
export let formatNumber = true;

$: progress = Math.round((used / max) * 100);

//TODO: depending on the data received, we could set the status
$: progress = Math.round((progressValue / progressMax) * 100);
</script>

<section class="progress-bar">
<div class="u-flex u-flex-vertical">
<div class="u-flex u-main-space-between">
<p>
<span class="heading-level-4">{used}</span>
<span class="body-text-1 u-bold">{unit}</span>
<span class="heading-level-4">{currentValue}</span>
<span class="body-text-1 u-bold">{currentUnit}</span>
</p>
<p class="heading-level-4">{progress}%</p>
</div>
<p class="body-text-2">
{formatNumber ? abbreviateNumber(max) : max}
{unit}
{maxValue}
{maxUnit}
</p>
</div>
<div
class="progress-bar-container u-margin-block-start-16"
class:is-warning={status === 'warning'}
class:is-danger={status === 'error'}
style:--graph-size={progress + '%'} />
{#if showBar}
<div
class="progress-bar-container u-margin-block-start-16"
class:is-warning={progress >= 75 && progress < 100}
class:is-danger={progress >= 100}
style:--graph-size={progress + '%'} />
{/if}
</section>
8 changes: 8 additions & 0 deletions src/lib/helpers/date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,11 @@ export function hoursToDays(hours: number) {
return `${hours} hour`;
}
}

export function getTomorrow(date: Date) {
const tomorrow = new Date(date);
tomorrow.setDate(tomorrow.getDate() + 2);
tomorrow.setHours(0, 0, 0, 0);

return tomorrow;
}
7 changes: 7 additions & 0 deletions src/lib/sdk/billing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ export type OrganizationUsage = {
executions: number;
storage: number;
users: Array<UsageMetric>;
projects: Array<{
projectId: string;
storage: number;
executions: number;
bandwidth: number;
users: number;
}>;
};

export type AggregationList = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@
import { page } from '$app/stores';
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';

export let data;

const tier = data?.currentInvoice?.tier ?? $organization?.billingPlan;
const plan = tierToPlan(tier).name;

let invoice = 'current';
let invoice = null;

async function handlePeriodChange() {
const target = invoice
Expand Down Expand Up @@ -56,7 +60,6 @@

<div class="u-flex u-gap-8 u-cross-center">
<p class="text">Usage period:</p>

<InputSelect
wrapperTag="div"
id="period"
Expand All @@ -67,10 +70,10 @@
options={[
{
label: 'Current billing cycle',
value: 'current'
value: null
},
...cycles
]}></InputSelect>
]} />
</div>
</div>

Expand All @@ -82,10 +85,28 @@
</p>

<svelte:fragment slot="aside">
{@const current = data.organizationUsage.bandwidth[0]?.value ?? 0}
{@const currentHumanized = humanFileSize(current)}
{@const max = getServiceLimit('bandwidth', tier)}
<ProgressBarBig
unit="GB"
max={getServiceLimit('bandwidth', tier)}
used={data.organizationUsage.bandwidth[0]?.value ?? 0} />
currentUnit={currentHumanized.unit}
currentValue={currentHumanized.value}
maxUnit="GB"
maxValue={max.toString()}
progressValue={bytesToSize(current, 'GB')}
progressMax={max}
showBar={false} />
<BarChart
series={[
{
name: 'Bandwidth',
data: [...data.organizationUsage.bandwidth.map((e) => [e.date, e.value])]
}
]} />
<ProjectBreakdown
projects={data.organizationUsage.projects}
metric="bandwidth"
{data} />
</svelte:fragment>
</CardGrid>

Expand All @@ -95,10 +116,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 max = getServiceLimit('users', tier)}
<ProgressBarBig
unit="Users"
max={getServiceLimit('users', tier)}
used={data.organizationUsage.bandwidth[0]?.value ?? 0} />
currentUnit="Users"
currentValue={current.toString()}
maxUnit="Users"
maxValue={abbreviateNumber(max)}
progressValue={current}
progressMax={max}
showBar={false} />
<BarChart
series={[
{
name: 'Users',
data: [...data.organizationUsage.users.map((e) => [e.date, e.value])]
}
]} />
<ProjectBreakdown projects={data.organizationUsage.projects} metric="users" {data} />
</svelte:fragment>
</CardGrid>

Expand All @@ -110,25 +145,42 @@
</p>

<svelte:fragment slot="aside">
{@const current = data.organizationUsage.executions}
{@const max = getServiceLimit('executions', tier)}
<ProgressBarBig
unit="Executions"
max={getServiceLimit('executions', tier)}
used={data.organizationUsage.executions} />
currentUnit="Executions"
currentValue={current.toString()}
maxUnit="Executions"
maxValue={abbreviateNumber(max)}
progressValue={current}
progressMax={max} />
<ProjectBreakdown
projects={data.organizationUsage.projects}
metric="executions"
{data} />
</svelte:fragment>
</CardGrid>

<CardGrid>
<Heading tag="h6" size="7">Storage</Heading>

<p class="text">
Calculated for all storage operations across all projects in your organization.
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 current = data.organizationUsage.storage}
{@const currentHumanized = humanFileSize(current)}
{@const max = getServiceLimit('storage', tier)}
<ProgressBarBig
unit="GB"
max={getServiceLimit('storage', tier)}
used={data.organizationUsage.storage} />
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} />
</svelte:fragment>
</CardGrid>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import { sdk } from '$lib/stores/sdk';
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 today = new Date().toISOString();
const tomorrow = getTomorrow(new Date());

let startDate: string;
let endDate: string;
let currentInvoice: Invoice | null = null;
Expand All @@ -18,16 +21,24 @@ export const load: PageLoad = async ({ params, parent }) => {
endDate = currentInvoice.to;
} else {
startDate = org.billingCurrentInvoiceDate;
endDate = today;
endDate = tomorrow.toISOString();
}

const [invoices, usage] = await Promise.all([
sdk.forConsole.billing.listInvoices(org.$id),
sdk.forConsole.billing.listUsage(params.organization, startDate, endDate)
]);

const projectNames = await sdk.forConsole.projects.list([
Query.equal(
'$id',
usage.projects.map((p) => p.projectId)
)
]);

return {
organizationUsage: usage,
projectNames: projectNames.projects,
invoices,
currentInvoice
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<script lang="ts">
import type { PageData } from './$types';
import { Collapsible, CollapsibleItem } from '$lib/components';
import {
TableBody,
TableCell,
TableCellLink,
TableCellHead,
TableHeader,
TableRow,
Table
} from '$lib/elements/table';
import { abbreviateNumber } from '$lib/helpers/numbers';
import { humanFileSize } from '$lib/helpers/sizeConvertion';
import type { OrganizationUsage } from '$lib/sdk/billing';

type Metric = 'users' | 'storage' | 'bandwidth' | 'executions';
export let data: PageData;
export let projects: OrganizationUsage['projects'];
export let metric: Metric;

function getProjectName(projectId: string): string {
return data.projectNames.find((project) => project.$id === projectId)?.name;
}

function getProjectUsageLink(projectId: string): string {
return `/console/project-${projectId}/settings/usage`;
}

function groupByProject(metric: Metric): Array<{ projectId: string; usage: number }> {
const data = [];
for (const project of projects) {
const usage = project[metric];
data.push({
projectId: project.projectId,
usage: usage ?? 0
});
}
return data;
}

function format(value: number): string {
switch (metric) {
case 'executions':
case 'users':
return abbreviateNumber(value);
case 'storage':
case 'bandwidth':
const humanized = humanFileSize(value);
return humanized.value + humanized.unit;
}
}
</script>

<Collapsible>
<CollapsibleItem>
<svelte:fragment slot="tgit restore --stageditle">Project breakdown</svelte:fragment>
<div class="table-wrapper">
<Table noMargin noStyles>
<TableHeader>
<TableCellHead width={285}>Project</TableCellHead>
<TableCellHead>Usage</TableCellHead>
<TableCellHead width={140} />
</TableHeader>
<TableBody>
{#each groupByProject(metric) as project}
<TableRow>
<TableCell title="Project">
{getProjectName(project.projectId)}
</TableCell>
<TableCell title="Usage">{format(project.usage)}</TableCell>
<TableCellLink
title="Go to project usage"
href={getProjectUsageLink(project.projectId)}
>View project usage</TableCellLink>
</TableRow>
{/each}
</TableBody>
</Table>
</div>
</CollapsibleItem>
</Collapsible>