mirror of
https://github.com/appwrite/console.git
synced 2026-04-07 19:17:46 +00:00
feat: add usage to deployments
This commit is contained in:
@@ -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 = '';
|
||||
|
||||
+194
-95
@@ -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}
|
||||
|
||||
+34
-3
@@ -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>
|
||||
|
||||
-121
@@ -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>
|
||||
Reference in New Issue
Block a user