feat: add usage to deployments

This commit is contained in:
Arman
2024-11-15 16:48:40 +01:00
parent 220c88f948
commit af142546e7
5 changed files with 266 additions and 225 deletions
+19 -6
View File
@@ -18,19 +18,32 @@
export let selectedRepository: string = null;
export let hasInstallations = false;
export let action: 'button' | 'select' = 'select';
export let installationList = $installations;
$: {
hasInstallations = $installations?.total > 0;
hasInstallations = installationList?.total > 0;
}
let selectedInstallation = null;
async function loadInstallations() {
const { installations } = await sdk.forProject.vcs.listInstallations();
if (installations.length) {
selectedInstallation = installations[0].$id;
installation.set(installations.find((entry) => entry.$id === selectedInstallation));
if (installationList) {
if (installationList.installations.length) {
selectedInstallation = installationList.installations[0].$id;
installation.set(
installationList.installations.find(
(entry) => entry.$id === selectedInstallation
)
);
}
return installationList.installations;
} else {
const { installations } = await sdk.forProject.vcs.listInstallations();
if (installations.length) {
selectedInstallation = installations[0].$id;
installation.set(installations.find((entry) => entry.$id === selectedInstallation));
}
return installations;
}
return installations;
}
let search = '';
@@ -1,9 +1,9 @@
<script lang="ts">
import { PaginationWithLimit, ViewSelector, EmptyFilter } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { Button, InputSelect } from '$lib/elements/forms';
import { deploymentList } from './store';
import { Container, ContainerHeader } from '$lib/layout';
import type { Models } from '@appwrite.io/console';
import { Container } from '$lib/layout';
import { SiteUsageRange, type Models } from '@appwrite.io/console';
import { writable } from 'svelte/store';
import type { Column } from '$lib/helpers/types';
import { Filters } from '$lib/components/filters';
@@ -14,8 +14,12 @@
import { Pill } from '$lib/elements';
import { onMount } from 'svelte';
import Table from './table.svelte';
import { Layout } from '@appwrite.io/pink-svelte';
import { Layout, Typography } from '@appwrite.io/pink-svelte';
import CreateGitDeploymentModal from './createGitDeploymentModal.svelte';
import UsageCard from './usageCard.svelte';
import { sdk } from '$lib/stores/sdk';
import { page } from '$app/stores';
import { toLocaleDate } from '$lib/helpers/date';
export let data;
@@ -47,29 +51,6 @@
format: 'string',
filter: false
},
{
id: 'type',
title: 'Source',
type: 'string',
show: true,
width: 90,
array: true,
format: 'enum',
elements: [
{ value: 'manual', label: 'Manual' },
{ value: 'cli', label: 'CLI' },
{ value: 'vcs', label: 'Git' }
]
},
{
id: '$updatedAt',
title: 'Updated',
type: 'datetime',
show: true,
width: 150,
format: 'datetime'
},
{
id: 'buildTime',
title: 'Build time',
@@ -94,7 +75,7 @@
},
{
id: 'size',
title: 'Size',
title: 'Total size',
type: 'integer',
show: true,
width: 140,
@@ -120,92 +101,210 @@
show: false,
filter: false,
width: 80
},
{
id: 'type',
title: 'Source',
type: 'string',
show: true,
width: 90,
array: true,
format: 'enum',
elements: [
{ value: 'manual', label: 'Manual' },
{ value: 'cli', label: 'CLI' },
{ value: 'vcs', label: 'Git' }
]
},
{
id: '$updatedAt',
title: 'Updated',
type: 'datetime',
show: true,
width: 150,
format: 'datetime'
}
]);
let showMobileFilters = false;
onMount(() => {
let range = SiteUsageRange.ThirtyDays;
const rangeOptions = [
{ value: SiteUsageRange.TwentyFourHours, label: 'Last 24 hours' },
{ value: SiteUsageRange.ThirtyDays, label: 'Last 30 days' },
{ value: SiteUsageRange.NinetyDays, label: 'Last 90 days' }
];
const now = new Date();
let metrics = [
{
id: 'buildsTotal',
value: null,
description: 'Total build count'
},
{
id: 'buildsStorageTotal',
value: null,
description: 'Total build size'
},
{
id: 'buildsTimeTotal',
value: null,
description: 'Total build time'
},
{
id: 'avgTime',
value: null,
description: 'Average build time'
},
{
id: 'success',
value: null,
description: 'Successful deployment'
},
{
id: 'failed',
value: null,
description: 'Failed deployment'
}
];
onMount(async () => {
data?.query ? (showMobileFilters = true) : (showMobileFilters = false);
await fetchUsage();
});
async function fetchUsage() {
console.log('test');
metrics.forEach((metric) => {
metric.value = null;
});
metrics = metrics;
try {
const usage = await sdk.forProject.sites.getSiteUsage($page.params.site, range);
metrics = metrics.map((metric) => {
metric.value = usage[metric.id];
return metric;
});
metrics = metrics;
console.log(usage);
} catch (error) {
console.log(error);
}
}
function clearAll() {
queries.clearAll();
queries.apply();
}
$: console.log(metrics);
</script>
<Container>
<ContainerHeader title="Deployments" />
<Layout.Stack justifyContent="space-between" direction="row">
<Layout.Stack alignItems="center" direction="row" gap="s">
<QuickFilters {columns} />
<Filters query={data.query} {columns} let:disabled let:toggle singleCondition>
<Layout.Stack alignItems="center" direction="row" gap="xs">
<Button text on:click={toggle} {disabled} size="s" ariaLabel="open filter">
<span class="icon-filter-line" />
<span class="text">More filters</span>
</Button>
{#if $tags?.length}
<!-- TODO: add vertical divider to pink 2 -->
<div
style="flex-basis:1px; background-color:hsl(var(--color-border)); width: 1px">
</div>
<Button text on:click={clearAll} size="s">Clear all</Button>
{/if}
<Layout.Stack gap="xxl">
<Layout.Stack gap="xl">
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography.Text variant="m-400" color="--color-fgcolor-neutral-tertiary">
Metrics for {range !== SiteUsageRange.TwentyFourHours
? `${toLocaleDate(
new Date(
now.getTime() -
parseInt(range.split('d')[0]) * 24 * 60 * 60 * 1000
).toString()
)} to`
: ''}
{toLocaleDate(now.toString())}
</Typography.Text>
<div>
<InputSelect
id="range"
bind:value={range}
options={rangeOptions}
on:change={fetchUsage} />
</div>
</Layout.Stack>
<Layout.Stack direction="row">
{#each metrics as metric}
<UsageCard description={metric.description} bind:value={metric.value} />
{/each}
</Layout.Stack>
</Layout.Stack>
<Layout.Stack gap="l">
<Layout.Stack justifyContent="space-between" direction="row">
<Layout.Stack alignItems="center" direction="row" gap="s">
<QuickFilters {columns} />
<Filters query={data.query} {columns} let:disabled let:toggle singleCondition>
<Layout.Stack alignItems="center" direction="row" gap="xs">
<Button
text
on:click={toggle}
{disabled}
size="s"
ariaLabel="open filter">
<span class="icon-filter-line" />
<span class="text">More filters</span>
</Button>
{#if $tags?.length}
<!-- TODO: add vertical divider to pink 2 -->
<div
style="flex-basis:1px; background-color:hsl(var(--color-border)); width: 1px">
</div>
<Button text on:click={clearAll} size="s">Clear all</Button>
{/if}
</Layout.Stack>
</Filters>
</Layout.Stack>
</Filters>
<ViewSelector view={View.Table} {columns} hideView allowNoColumns hideText />
<Button size="s" on:click={() => (showCreateDeployment = true)}
>Create deployment</Button>
</Layout.Stack>
<div class="is-only-mobile">
<Layout.Stack justifyContent="space-between" direction="row">
<Button
text
size="s"
on:click={() => (showMobileFilters = !showMobileFilters)}
ariaLabel="toggle filters">
<span class="icon-filter-line" />
<span class="text">Filters</span>
</Button>
<ViewSelector view={View.Table} {columns} hideView allowNoColumns />
</Layout.Stack>
<div
class:u-hide={!showMobileFilters}
class:u-flex={showMobileFilters}
class=" u-gap-8 u-flex-wrap u-margin-block-start-16">
<QuickFilters {columns} />
<Filters query={data.query} {columns} clearOnClick>
<svelte:fragment slot="mobile" let:disabled let:toggle>
<Pill
button
on:click={toggle}
{disabled}
class="u-flex u-gap-4 u-cross-center"
style="--p-tag-content-height: auto">
<span class="text">More filters</span>
<span class="icon-cheveron-down" />
</Pill>
</svelte:fragment>
</Filters>
</div>
</div>
{#if $deploymentList.total}
<Table columns={$columns} {data} />
<PaginationWithLimit
name="Deployments"
limit={data.limit}
offset={data.offset}
total={$deploymentList?.total} />
{:else if data?.query}
<EmptyFilter resource="deployments" />
{/if}
</Layout.Stack>
<ViewSelector view={View.Table} {columns} hideView allowNoColumns hideText />
<Button size="s" on:click={() => (showCreateDeployment = true)}>Create deployment</Button>
</Layout.Stack>
<div class="is-only-mobile">
<Layout.Stack justifyContent="space-between" direction="row">
<Button
text
size="s"
on:click={() => (showMobileFilters = !showMobileFilters)}
ariaLabel="toggle filters">
<span class="icon-filter-line" />
<span class="text">Filters</span>
</Button>
<ViewSelector view={View.Table} {columns} hideView allowNoColumns />
</Layout.Stack>
<div
class:u-hide={!showMobileFilters}
class:u-flex={showMobileFilters}
class=" u-gap-8 u-flex-wrap u-margin-block-start-16">
<QuickFilters {columns} />
<Filters query={data.query} {columns} clearOnClick>
<svelte:fragment slot="mobile" let:disabled let:toggle>
<Pill
button
on:click={toggle}
{disabled}
class="u-flex u-gap-4 u-cross-center"
style="--p-tag-content-height: auto">
<span class="text">More filters</span>
<span class="icon-cheveron-down" />
</Pill>
</svelte:fragment>
</Filters>
</div>
</div>
{#if $deploymentList.total}
<Table columns={$columns} {data} />
{:else if data?.query}
<EmptyFilter resource="deployments" />
{/if}
<PaginationWithLimit
name="Deployments"
limit={data.limit}
offset={data.offset}
total={$deploymentList?.total} />
</Container>
{#if selectedDeployment}
<RedeployModal {selectedDeployment} bind:show={showRedeploy} />
{/if}
@@ -1,10 +1,23 @@
<script lang="ts">
import { Modal } from '$lib/components';
import { Modal, Card } from '$lib/components';
import Repositories from '$lib/components/repositories.svelte';
import { Link } from '$lib/elements';
import { Button } from '$lib/elements/forms';
import { InlineCode, Layout } from '@appwrite.io/pink-svelte';
import { sdk } from '$lib/stores/sdk';
import { IconGithub } from '@appwrite.io/pink-icons-svelte';
import { Icon, InlineCode, Layout, Typography } from '@appwrite.io/pink-svelte';
import { onMount } from 'svelte';
export let show = false;
let installations = { installations: [], total: 0 };
let selectedRepository: string = null;
onMount(async () => {
installations = await sdk.forProject.vcs.listInstallations();
console.log(installations);
});
</script>
<Modal title="Create Git deployment" bind:show>
@@ -12,7 +25,25 @@
Enter a valid commit reference to create a new deployment from <InlineCode code="test" /> or
use the CLI to deploy. <Link href="#">Learn more</Link>
</span>
<Layout.Stack direction="row" gap="m" wrap="wrap"></Layout.Stack>
<Card isTile padding="s" radius="s">
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center" gap="xs">
<Layout.Stack direction="row" alignItems="center" gap="s">
<Icon size="s" icon={IconGithub} />
<Typography.Text variant="m-400" color="--color-fgcolor-neutral-primary">
name
</Typography.Text>
</Layout.Stack>
</Layout.Stack>
</Card>
<Repositories
bind:selectedRepository
installationList={installations}
action="button"
callbackState={{
from: 'github',
to: 'cover'
}}
on:connect={() => console.log('test')} />
<svelte:fragment slot="footer">
<Button text size="s" on:click={() => (show = false)}>Cancel</Button>
<Button size="s">Next</Button>
@@ -1,121 +0,0 @@
<script lang="ts">
import { CardGrid, Id, SvgIcon } from '$lib/components';
import type { Models } from '@appwrite.io/console';
import { func, proxyRuleList } from './store';
import { Pill } from '$lib/elements';
import { humanFileSize } from '$lib/helpers/sizeConvertion';
import { calculateTime } from '$lib/helpers/timeConversion';
import DeploymentCreatedBy from './deploymentCreatedBy.svelte';
import DeploymentSource from './deploymentSource.svelte';
import DeploymentDomains from './deploymentDomains.svelte';
import { tooltip } from '$lib/actions/tooltip';
export let deployment: Models.Deployment;
</script>
<CardGrid>
<div class="u-flex u-cross-start u-gap-16">
<div class="avatar" style={`--p-image-size: ${32 / 16}rem`} aria-hidden="true">
<SvgIcon size={64} iconSize="large" name={$func.runtime.split('-')[0]}></SvgIcon>
</div>
<div class="u-grid-equal-row-size u-gap-4 u-line-height-1">
<p><b>Deployment ID</b></p>
<Id value={deployment.$id}>
{deployment.$id}
</Id>
</div>
</div>
<svelte:fragment slot="aside">
{@const status = deployment.status}
{@const deploymentSize = humanFileSize(deployment.size)}
{@const buildSize = humanFileSize(deployment.buildSize)}
{@const totalSize = humanFileSize(deployment.buildSize + deployment.size)}
<ul class="stats-grid-box u-gap-16">
<li class="u-flex-vertical u-gap-4">
<p class="u-color-text-offline">Status</p>
<span>
<Pill
danger={status === 'failed'}
warning={status === 'building'}
success={status === 'ready'}>
<span class="icon-lightning-bolt" aria-hidden="true" />
<span class="text u-trim">
{status === 'ready' ? 'active' : status}
</span>
</Pill>
</span>
</li>
<li class="u-flex-vertical u-gap-4">
<p class="u-color-text-offline">Build time</p>
<p class="u-line-height-2">
{calculateTime(deployment.buildTime)}
</p>
</li>
<li class="u-flex-vertical u-gap-4">
<p class="u-color-text-offline">Total size</p>
<p class="u-line-height-2">
{totalSize.value + totalSize.unit}
<button
type="button"
on:click|preventDefault
class="tooltip"
aria-label="input tooltip"
use:tooltip={{
content: `
<p><b>Deployment size:</b> ${deploymentSize.value + deploymentSize.unit}</p>
<p><b>Build size:</b> ${buildSize.value + buildSize.unit}</p>
`,
allowHTML: true,
appendTo: 'parent'
}}>
<span
class="icon-info"
aria-hidden="true"
style="font-size: var(--icon-size-small)" />
</button>
</p>
</li>
<li class="u-flex-vertical u-gap-4">
<p class="u-color-text-offline">Updated</p>
<p class="u-line-height-2">
<DeploymentCreatedBy {deployment} />
</p>
</li>
</ul>
<div class="u-flex u-flex-vertical u-gap-4">
<p class="u-color-text-offline">Source</p>
<div>
<DeploymentSource {deployment} />
</div>
</div>
{#if $proxyRuleList?.rules?.length}
<div class="u-flex u-flex-vertical u-gap-4">
<p class="u-color-text-offline">Domains</p>
<DeploymentDomains domain={$proxyRuleList} />
</div>
{/if}
</svelte:fragment>
<svelte:fragment slot="actions">
<slot name="actions" />
</svelte:fragment>
</CardGrid>
<style lang="scss">
@import '@appwrite.io/pink/src/abstract/variables/_devices.scss';
.stats-grid-box {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
@media #{$break3open} {
.stats-grid-box {
grid-template-columns: repeat(4, 1fr);
}
}
</style>
@@ -0,0 +1,19 @@
<script lang="ts">
import { Card } from '$lib/components';
import { Layout, Skeleton, Typography } from '@appwrite.io/pink-svelte';
export let description: string;
export let value: string = null;
</script>
<Card radius="s" padding="xs" isTile>
<Layout.Stack gap="xxs">
{#if value !== null && value !== undefined}
<Typography.Title size="s">{value}</Typography.Title>
{:else}
<Skeleton variant="line" width={100} height={26} />
{/if}
<Typography.Text variant="m-400" color="--color-fgcolor-neutral-tertiary">
{description}
</Typography.Text>
</Layout.Stack>
</Card>