feat: usage for databases

This commit is contained in:
Torsten Dittmann
2022-10-11 12:56:27 +02:00
parent 4a03a7528f
commit 4d6ecf009e
14 changed files with 433 additions and 392 deletions
+1
View File
@@ -10,3 +10,4 @@ export { default as Wizard } from './wizard.svelte';
export { default as WizardStep } from './wizardStep.svelte';
export { default as Breadcrumbs } from './breadcrumbs.svelte';
export { default as Unauthenticated } from './unauthenticated.svelte';
export { default as Usage, type UsagePeriods } from './usage.svelte';
+141
View File
@@ -0,0 +1,141 @@
<script context="module" lang="ts">
export type UsagePeriods = '24h' | '30d' | '90d';
</script>
<script lang="ts">
import { Container } from '$lib/layout';
import { BarChart, LineChart } from '$lib/charts';
import { Card, Tiles } from '$lib/components';
import type { Models } from '@aw-labs/appwrite-console';
type MetricMetadata = {
title: string;
legend: string;
};
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 range: UsagePeriods;
function last(set: Models.Metric[]): Models.Metric | null {
return set.slice(-1)[0] ?? null;
}
function total(set: Models.Metric[]): number {
return set.reduce((prev, curr) => prev + curr.value, 0);
}
</script>
<Container>
<div class="u-flex u-main-space-between common-section">
<h2 class="heading-level-5">Users</h2>
<ul class="drop-tabs">
<li class="drop-tabs-item">
<button
class="drop-tabs-button"
on:click={() => (range = '24h')}
disabled={range === '24h'}>
<span class="text">24h</span>
</button>
</li>
<li class="drop-tabs-item">
<button
class="drop-tabs-button"
on:click={() => (range = '30d')}
disabled={range === '30d'}>
<span class="text">30d</span>
</button>
</li>
<li class="drop-tabs-item">
<button
class="drop-tabs-button"
on:click={() => (range = '90d')}
disabled={range === '90d'}>
<span class="text">90d</span>
</button>
</li>
</ul>
</div>
<Card>
{#if count}
<h6 class="heading-level-6">{last(count).value}</h6>
<p>{countMetadata.title}</p>
<div class="u-margin-block-start-16" />
<BarChart
series={[
{
name: countMetadata.legend,
data: [...count.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Tiles>
<Card isTile>
{#if created}
<h6 class="heading-level-6">{total(created)}</h6>
<p>{createdMetadata.title}</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: createdMetadata.legend,
data: [...created.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Card isTile>
{#if read}
<h6 class="heading-level-6">{total(read)}</h6>
<p>{createdMetadata.title}</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: readMetadata.legend,
data: [...read.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Card isTile>
{#if updated}
<h6 class="heading-level-6">{total(updated)}</h6>
<p>{updatedMetadata.title}</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: updatedMetadata.legend,
data: [...updated.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Card isTile>
{#if deleted}
<h6 class="heading-level-6">{total(deleted)}</h6>
<p>{deletedMetadata.title}</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: deletedMetadata.legend,
data: [...deleted.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
</Tiles>
</Container>
@@ -1,133 +1,45 @@
<script lang="ts">
import { Container } from '$lib/layout';
import { BarChart, LineChart } from '$lib/charts';
import { Card, Tiles } from '$lib/components';
import { Usage } from '$lib/layout';
import { usersUsage } from '../store';
import type { Models } from '@aw-labs/appwrite-console';
import type { UsagePeriods } from '$lib/layout/usage.svelte';
let currentRange = '30d';
let range: UsagePeriods = '30d';
$: usersUsage.load(currentRange);
$: usersUsage.load(range);
// TODO: metric type is wrong
$: usersCount = $usersUsage?.usersCount as unknown as Models.Metric[];
$: usersCreate = $usersUsage?.usersCreate as unknown as Models.Metric[];
$: usersRead = $usersUsage?.usersRead as unknown as Models.Metric[];
$: usersUpdate = $usersUsage?.usersUpdate as unknown as Models.Metric[];
$: usersDelete = $usersUsage?.usersDelete as unknown as Models.Metric[];
function last(set: Array<unknown>): Models.Metric | null {
return (set as Models.Metric[]).slice(-1)[0] ?? null;
}
// TODO: metric type is wrong
function total(set: Array<unknown>): number {
return (set as Models.Metric[]).reduce((prev, curr) => prev + curr.value, 0);
}
$: count = $usersUsage?.usersCount as unknown as Models.Metric[];
$: created = $usersUsage?.usersCreate as unknown as Models.Metric[];
$: read = $usersUsage?.usersRead as unknown as Models.Metric[];
$: updated = $usersUsage?.usersUpdate as unknown as Models.Metric[];
$: deleted = $usersUsage?.usersDelete as unknown as Models.Metric[];
</script>
<Container>
<div class="u-flex u-main-space-between common-section">
<h2 class="heading-level-5">Users</h2>
<ul class="drop-tabs">
<li class="drop-tabs-item">
<button
class="drop-tabs-button"
on:click={() => (currentRange = '24h')}
disabled={currentRange === '24h'}>
<span class="text">24h</span>
</button>
</li>
<li class="drop-tabs-item">
<button
class="drop-tabs-button"
on:click={() => (currentRange = '30d')}
disabled={currentRange === '30d'}>
<span class="text">30d</span>
</button>
</li>
<li class="drop-tabs-item">
<button
class="drop-tabs-button"
on:click={() => (currentRange = '90d')}
disabled={currentRange === '90d'}>
<span class="text">90d</span>
</button>
</li>
</ul>
</div>
{#if $usersUsage}
<Card>
{#if usersCount}
<h6 class="heading-level-6">{last(usersCount).value}</h6>
<p>Registered users</p>
<div class="u-margin-block-start-16" />
<BarChart
series={[
{
name: 'User',
data: [...usersCount.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Tiles>
<Card isTile>
{#if usersCreate}
<h6 class="heading-level-6">{total(usersCreate)}</h6>
<p>Users created</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: 'Create',
data: [...usersCreate.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Card isTile>
{#if usersRead}
<h6 class="heading-level-6">{total(usersRead)}</h6>
<p>Users read</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: 'Read',
data: [...usersRead.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Card isTile>
{#if usersUpdate}
<h6 class="heading-level-6">{total(usersUpdate)}</h6>
<p>Users updated</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: 'Update',
data: [...usersUpdate.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Card isTile>
{#if usersDelete}
<h6 class="heading-level-6">{total(usersDelete)}</h6>
<p>Users deleted</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: 'Delete',
data: [...usersDelete.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
</Tiles>
{/if}
</Container>
<Usage
bind:range
{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'
}} />
@@ -46,6 +46,10 @@
href: `${path}/indexes`,
title: 'Indexes'
},
{
href: `${path}/usage`,
title: 'Usage'
},
{
href: `${path}/settings`,
title: 'Settings'
@@ -116,3 +116,17 @@ export const attributeList = cachedStore<
}
};
});
export const collectionUsage = cachedStore<
Models.UsageCollection,
{
load: (databaseId: string, collectionId: string, range: string) => Promise<void>;
}
>('collectionUsage', function ({ set }) {
return {
load: async (databaseId, range) => {
const usages = await sdkForProject.databases.getCollectionUsage(databaseId, range);
set(usages);
}
};
});
@@ -0,0 +1,48 @@
<script lang="ts">
import { page } from '$app/stores';
import { Usage, type UsagePeriods } from '$lib/layout';
import { collectionUsage } from '../store';
import type { Models } from '@aw-labs/appwrite-console';
let range: UsagePeriods = '30d';
const databaseId = $page.params.database;
const collectionId = $page.params.collection;
$: collectionUsage.load(databaseId, collectionId, range);
// TODO: metric type is wrong
$: count = $collectionUsage?.documentsCount as unknown as Models.Metric[];
$: created = $collectionUsage?.documentsCreate as unknown as Models.Metric[];
$: read = $collectionUsage?.documentsRead as unknown as Models.Metric[];
$: updated = $collectionUsage?.documentsUpdate as unknown as Models.Metric[];
$: deleted = $collectionUsage?.documentsDelete as unknown as Models.Metric[];
</script>
<Usage
bind:range
{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'
}} />
@@ -38,3 +38,17 @@ export const database = cachedStore<
}
};
});
export const databaseUsage = cachedStore<
Models.UsageDatabase,
{
load: (databaseId: string, range: string) => Promise<void>;
}
>('databaseUsage', function ({ set }) {
return {
load: async (databaseId, range) => {
const usages = await sdkForProject.databases.getDatabaseUsage(databaseId, range);
set(usages);
}
};
});
@@ -1,9 +1,47 @@
<script>
import { Card } from '$lib/components';
import { Container } from '$lib/layout';
<script lang="ts">
import { page } from '$app/stores';
import { Usage, type UsagePeriods } from '$lib/layout';
import { databaseUsage } from '../store';
import type { Models } from '@aw-labs/appwrite-console';
let range: UsagePeriods = '30d';
const databaseId = $page.params.database;
$: databaseUsage.load(databaseId, range);
// TODO: metric type is wrong
$: count = $databaseUsage?.collectionsCount as unknown as Models.Metric[];
$: created = $databaseUsage?.collectionsCreate as unknown as Models.Metric[];
$: read = $databaseUsage?.collectionsRead as unknown as Models.Metric[];
$: updated = $databaseUsage?.collectionsUpdate as unknown as Models.Metric[];
$: deleted = $databaseUsage?.collectionsDelete as unknown as Models.Metric[];
</script>
<Container>
<h1>Usage</h1>
<Card />
</Container>
<Usage
bind:range
{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'
}} />
@@ -1,6 +1,6 @@
import { sdkForProject } from '$lib/stores/sdk';
import type { Models } from '@aw-labs/appwrite-console';
import { cachedStore } from '$lib/helpers/cache';
import type { Models } from '@aw-labs/appwrite-console';
export const databaseList = cachedStore<
Models.DatabaseList,
@@ -15,3 +15,17 @@ export const databaseList = cachedStore<
}
};
});
export const databasesUsage = cachedStore<
Models.UsageDatabases,
{
load: (range: string) => Promise<void>;
}
>('databasesUsage', function ({ set }) {
return {
load: async (range) => {
const usages = await sdkForProject.databases.getUsage(range);
set(usages);
}
};
});
@@ -1,10 +1,44 @@
<script>
import { Card } from '$lib/components';
import { Container } from '$lib/layout';
<script lang="ts">
import { Usage, type UsagePeriods } from '$lib/layout';
import { databasesUsage } from '../store';
import type { Models } from '@aw-labs/appwrite-console';
let range: UsagePeriods = '30d';
$: databasesUsage.load(range);
// TODO: metric type is wrong
$: count = $databasesUsage?.databasesCount as unknown as Models.Metric[];
$: created = $databasesUsage?.databasesCreate as unknown as Models.Metric[];
$: read = $databasesUsage?.databasesRead as unknown as Models.Metric[];
$: updated = $databasesUsage?.databasesUpdate as unknown as Models.Metric[];
$: deleted = $databasesUsage?.databasesDelete as unknown as Models.Metric[];
</script>
<Container>
<h1>Usage</h1>
<Card />
</Container>
<Usage
bind:range
{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'
}} />
@@ -9,7 +9,7 @@
import { usage, type UsagePeriods } from './store';
import { onMount } from 'svelte';
import { afterNavigate } from '$app/navigation';
import { DropList, DropListItem } from '$lib/components';
import { Code, DropList, DropListItem } from '$lib/components';
import { BarChart, LineChart } from '$lib/charts';
import { humanFileSize } from '$lib/helpers/sizeConvertion';
@@ -2,11 +2,10 @@ import { sdkForConsole } from '$lib/stores/sdk';
import { cachedStore } from '$lib/helpers/cache';
import type { Models } from '@aw-labs/appwrite-console';
export type UsagePeriods = '24h' | '30d' | '90d';
export const usage = cachedStore<
Models.UsageProject,
{
load: (projectId: string, range: UsagePeriods) => Promise<void>;
load: (projectId: string, range: string) => Promise<void>;
}
>('projectUsage', function ({ set }) {
return {
@@ -1,136 +1,47 @@
<script lang="ts">
import { Container } from '$lib/layout';
import { BarChart, LineChart } from '$lib/charts';
import { Card, Tiles } from '$lib/components';
import { page } from '$app/stores';
import { Usage, type UsagePeriods } from '$lib/layout';
import { bucketUsage } from '../store';
import type { Models } from '@aw-labs/appwrite-console';
import { page } from '$app/stores';
let currentRange = '30d';
let range: UsagePeriods = '30d';
const bucketId = $page.params.bucket;
$: bucketUsage.load(bucketId, currentRange);
$: bucketUsage.load(bucketId, range);
// TODO: metric type is wrong
$: filesCount = $bucketUsage?.filesCount as unknown as Models.Metric[];
$: filesCreate = $bucketUsage?.filesCreate as unknown as Models.Metric[];
$: filesRead = $bucketUsage?.filesRead as unknown as Models.Metric[];
$: filesUpdate = $bucketUsage?.filesUpdate as unknown as Models.Metric[];
$: filesDelete = $bucketUsage?.filesDelete as unknown as Models.Metric[];
function last(set: Array<unknown>): Models.Metric | null {
return (set as Models.Metric[]).slice(-1)[0] ?? null;
}
// TODO: metric type is wrong
function total(set: Array<unknown>): number {
return (set as Models.Metric[]).reduce((prev, curr) => prev + curr.value, 0);
}
$: count = $bucketUsage?.filesCount as unknown as Models.Metric[];
$: created = $bucketUsage?.filesCreate as unknown as Models.Metric[];
$: read = $bucketUsage?.filesRead as unknown as Models.Metric[];
$: updated = $bucketUsage?.filesUpdate as unknown as Models.Metric[];
$: deleted = $bucketUsage?.filesDelete as unknown as Models.Metric[];
</script>
<Container>
<div class="u-flex u-main-space-between common-section">
<h2 class="heading-level-5">Users</h2>
<ul class="drop-tabs">
<li class="drop-tabs-item">
<button
class="drop-tabs-button"
on:click={() => (currentRange = '24h')}
disabled={currentRange === '24h'}>
<span class="text">24h</span>
</button>
</li>
<li class="drop-tabs-item">
<button
class="drop-tabs-button"
on:click={() => (currentRange = '30d')}
disabled={currentRange === '30d'}>
<span class="text">30d</span>
</button>
</li>
<li class="drop-tabs-item">
<button
class="drop-tabs-button"
on:click={() => (currentRange = '90d')}
disabled={currentRange === '90d'}>
<span class="text">90d</span>
</button>
</li>
</ul>
</div>
{#if $bucketUsage}
<Card>
{#if filesCount}
<h6 class="heading-level-6">{last(filesCount).value}</h6>
<p>Total Files</p>
<div class="u-margin-block-start-16" />
<BarChart
series={[
{
name: 'User',
data: [...filesCount.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Tiles>
<Card isTile>
{#if filesCreate}
<h6 class="heading-level-6">{total(filesCreate)}</h6>
<p>Files created</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: 'Create',
data: [...filesCreate.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Card isTile>
{#if filesRead}
<h6 class="heading-level-6">{total(filesRead)}</h6>
<p>Files read</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: 'Read',
data: [...filesRead.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Card isTile>
{#if filesUpdate}
<h6 class="heading-level-6">{total(filesUpdate)}</h6>
<p>Files updated</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: 'Update',
data: [...filesUpdate.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Card isTile>
{#if filesDelete}
<h6 class="heading-level-6">{total(filesDelete)}</h6>
<p>Files deleted</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: 'Delete',
data: [...filesDelete.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
</Tiles>
{/if}
</Container>
<Usage
bind:range
{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'
}} />
@@ -1,133 +1,44 @@
<script lang="ts">
import { Container } from '$lib/layout';
import { BarChart, LineChart } from '$lib/charts';
import { Card, Tiles } from '$lib/components';
import { Usage, type UsagePeriods } from '$lib/layout';
import { storageUsage } from '../store';
import type { Models } from '@aw-labs/appwrite-console';
let currentRange = '30d';
let range: UsagePeriods = '30d';
$: storageUsage.load(currentRange);
$: storageUsage.load(range);
// TODO: metric type is wrong
$: bucketsCount = $storageUsage?.bucketsCount as unknown as Models.Metric[];
$: bucketsCreate = $storageUsage?.bucketsCreate as unknown as Models.Metric[];
$: bucketsRead = $storageUsage?.bucketsRead as unknown as Models.Metric[];
$: bucketsUpdate = $storageUsage?.bucketsUpdate as unknown as Models.Metric[];
$: bucketsDelete = $storageUsage?.bucketsDelete as unknown as Models.Metric[];
function last(set: Array<unknown>): Models.Metric | null {
return (set as Models.Metric[]).slice(-1)[0] ?? null;
}
// TODO: metric type is wrong
function total(set: Array<unknown>): number {
return (set as Models.Metric[]).reduce((prev, curr) => prev + curr.value, 0);
}
$: count = $storageUsage?.bucketsCount as unknown as Models.Metric[];
$: created = $storageUsage?.bucketsCreate as unknown as Models.Metric[];
$: read = $storageUsage?.bucketsRead as unknown as Models.Metric[];
$: updated = $storageUsage?.bucketsUpdate as unknown as Models.Metric[];
$: deleted = $storageUsage?.bucketsDelete as unknown as Models.Metric[];
</script>
<Container>
<div class="u-flex u-main-space-between common-section">
<h2 class="heading-level-5">Users</h2>
<ul class="drop-tabs">
<li class="drop-tabs-item">
<button
class="drop-tabs-button"
on:click={() => (currentRange = '24h')}
disabled={currentRange === '24h'}>
<span class="text">24h</span>
</button>
</li>
<li class="drop-tabs-item">
<button
class="drop-tabs-button"
on:click={() => (currentRange = '30d')}
disabled={currentRange === '30d'}>
<span class="text">30d</span>
</button>
</li>
<li class="drop-tabs-item">
<button
class="drop-tabs-button"
on:click={() => (currentRange = '90d')}
disabled={currentRange === '90d'}>
<span class="text">90d</span>
</button>
</li>
</ul>
</div>
{#if $storageUsage}
<Card>
{#if bucketsCount}
<h6 class="heading-level-6">{last(bucketsCount).value}</h6>
<p>Total Buckets</p>
<div class="u-margin-block-start-16" />
<BarChart
series={[
{
name: 'User',
data: [...bucketsCount.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Tiles>
<Card isTile>
{#if bucketsCreate}
<h6 class="heading-level-6">{total(bucketsCreate)}</h6>
<p>Buckets created</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: 'Create',
data: [...bucketsCreate.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Card isTile>
{#if bucketsRead}
<h6 class="heading-level-6">{total(bucketsRead)}</h6>
<p>Buckets read</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: 'Read',
data: [...bucketsRead.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Card isTile>
{#if bucketsUpdate}
<h6 class="heading-level-6">{total(bucketsUpdate)}</h6>
<p>Buckets updated</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: 'Update',
data: [...bucketsUpdate.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
<Card isTile>
{#if bucketsDelete}
<h6 class="heading-level-6">{total(bucketsDelete)}</h6>
<p>Buckets deleted</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: 'Delete',
data: [...bucketsDelete.map((e) => [e.date, e.value])]
}
]} />
{/if}
</Card>
</Tiles>
{/if}
</Container>
<Usage
bind:range
{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'
}} />