Merge branch 'feat-pink-v2' of https://github.com/appwrite/console into feat-pink-v2

This commit is contained in:
Torsten Dittmann
2025-03-12 20:36:53 +04:00
32 changed files with 586 additions and 680 deletions
+1 -2
View File
@@ -1,7 +1,6 @@
export { default as Filters } from './filters.svelte';
export { default as TagList } from './tagList.svelte';
export { default as FilterMenu } from './menu.svelte';
export { default as FilterSubMenu } from './subMenu.svelte';
export { default as CustomFilters } from './customFilters.svelte';
export { default as QuickFilters } from './quickFilters.svelte';
export { default as ParsedTagList } from './parsedTagList.svelte';
export { hasPageQueries, queryParamToMap, queries } from '$lib/components/filters/store';
@@ -1,22 +1,23 @@
<script lang="ts">
import { Icon, Layout, Tag, Tooltip } from '@appwrite.io/pink-svelte';
import { queries, tagFormat } from './store';
import { queries, tagFormat, tags } from './store';
import { IconX } from '@appwrite.io/pink-icons-svelte';
import { parsedTags } from './setFilters';
import { Button } from '$lib/elements/forms';
$: console.log($parsedTags);
</script>
{#if $parsedTags?.length}
<Layout.Stack direction="row" gap="s" wrap="wrap" alignItems="center" inline>
{#each $parsedTags as tag}
<span>
<Tooltip disabled={Array.isArray(tag.value) ? tag.value?.length < 3 : true}>
<Tooltip
disabled={Array.isArray(tag.value) ? tag.value?.length < 3 : true}
maxWidth="600px">
<Tag
size="s"
on:click={() => {
queries.removeFilter(tag);
const t = $tags.filter((t) => t.tag.includes(tag.tag.split(' ')[0]));
t.forEach((t) => (t ? queries.removeFilter(t) : null));
queries.apply();
parsedTags.update((tags) => tags.filter((t) => t.tag !== tag.tag));
}}>
+54 -38
View File
@@ -1,13 +1,16 @@
<script lang="ts">
import { afterNavigate } from '$app/navigation';
import MenuItem from '$lib/components/filters/subMenu.svelte';
import { queryParamToMap } from '$lib/components/filters/store';
import type { Column } from '$lib/helpers/types';
import { type Writable } from 'svelte/store';
import Menu from '$lib/components/filters/menu.svelte';
import { CustomFilters } from '$lib/components/filters';
import { setFiltersOnNavigate } from '$lib/components/filters/setFilters';
import { addFilterAndApply, buildFilterCol } from '$lib/components/filters/quickFilters';
import { addFilterAndApply, buildFilterCol } from './quickFilters';
import { parsedTags, setFiltersOnNavigate } from './setFilters';
import Menu from '../menu/menu.svelte';
import { Button } from '$lib/elements/forms';
import { Icon } from '@appwrite.io/pink-svelte';
import { IconFilterLine } from '@appwrite.io/pink-icons-svelte';
import QuickfiltersSubMenu from './quickfiltersSubMenu.svelte';
export let columns: Writable<Column[]>;
export let analyticsSource: string;
@@ -22,44 +25,57 @@
const localTags = Array.from(localQueries.keys());
setFiltersOnNavigate(localTags, filterCols, $columns);
filterCols = filterCols;
});
</script>
<Menu>
{#each filterCols as filter}
{#if filter.options}
<MenuItem
{filter}
variant={filter?.array ? 'checkbox' : 'radio'}
on:add={(e) => {
addFilterAndApply(
filter.id,
filter.title,
filter.operator,
e.detail.value,
filter?.array
? (filter.options
.filter((opt) => opt.checked)
.map((opt) => opt.value) ?? [])
: [],
$columns,
analyticsSource
);
}}
on:clear={() => {
filter.tag = null;
addFilterAndApply(
filter.id,
filter.title,
filter.operator,
null,
[],
$columns,
analyticsSource
);
}} />
{/if}
{/each}
{#if $parsedTags?.length}
<Button secondary badge={`${$parsedTags?.length}`}>
<Icon icon={IconFilterLine} slot="start" size="s" />
Filters
</Button>
{:else}
<Button secondary>
<Icon icon={IconFilterLine} slot="start" size="s" />
Filters
</Button>
{/if}
<svelte:fragment slot="menu">
{#each filterCols as filter}
{#if filter.options}
<QuickfiltersSubMenu
{filter}
variant={filter?.array ? 'checkbox' : 'radio'}
on:add={(e) => {
addFilterAndApply(
filter.id,
filter.title,
filter.operator,
e.detail.value,
filter?.array
? (filter.options
.filter((opt) => opt.checked)
.map((opt) => opt.value) ?? [])
: [],
$columns,
analyticsSource
);
}}
on:clear={() => {
addFilterAndApply(
filter.id,
filter.title,
filter.operator,
null,
[],
$columns,
analyticsSource
);
}} />
{/if}
{/each}
</svelte:fragment>
<svelte:fragment slot="end">
<CustomFilters {columns} />
@@ -61,12 +61,6 @@ export function addFilterAndApply(
});
}
export function resetOptions(filter: FilterData) {
filter.options.forEach((option) => {
option.checked = false;
});
}
export function addStatusCodeFilter(value: string, colId: string, columns: Column[]) {
addFilter(columns, colId, ValidOperators.LessThanOrEqual, parseInt(value));
addFilter(columns, colId, ValidOperators.GreaterThanOrEqual, parseInt(value) - 99);
@@ -4,17 +4,7 @@
import { ActionMenu, Card, Layout, Selector } from '@appwrite.io/pink-svelte';
import { createMenubar, melt } from '@melt-ui/svelte';
import { createEventDispatcher } from 'svelte';
import type { ValidOperators } from './store';
type FilterData = {
title: string;
id: string;
array: boolean;
show: boolean;
tag: string;
operator: ValidOperators;
options: { value: string; label: string; checked: boolean }[];
};
import type { FilterData } from './quickFilters';
export let filter: FilterData;
export let variant: 'checkbox' | 'radio' = 'checkbox';
@@ -44,7 +34,7 @@
</script>
<div use:melt={$subTrigger}>
<ActionMenu.Root>
<ActionMenu.Root noPadding>
<ActionMenu.Item.Button trailingIcon={IconChevronRight}
>{filter.title}</ActionMenu.Item.Button>
</ActionMenu.Root>
@@ -61,11 +51,7 @@
on:click={() => {
option.checked = !option.checked;
dispatch('add', {
value: filter?.array
? null
: option.checked
? option.value
: null
value: option.value
});
}}>
{capitalize(option.label)}
@@ -79,15 +65,11 @@
on:click={() => {
option.checked = !option.checked;
dispatch('add', {
value: filter?.array
? null
: option.checked
? option.value
: null
value: option.checked
});
}}>
<Layout.Stack direction="row" gap="s">
<Selector.Checkbox bind:checked={option.checked} size="s" />
<Selector.Checkbox checked={option.checked} size="s" />
{capitalize(option.label)}
</Layout.Stack>
</ActionMenu.Item.Button>
+41 -20
View File
@@ -1,24 +1,25 @@
import type { Column } from '$lib/helpers/types';
import { get, writable } from 'svelte/store';
import { resetOptions, type FilterData } from './quickFilters';
import { type FilterData } from './quickFilters';
import { tags, type TagValue } from './store';
export const parsedTags = writable<TagValue[]>([]);
export function setFiltersOnNavigate(
tags: TagValue[],
localTags: TagValue[],
filterCols: FilterData[],
$columns: Column[]
) {
if (!tags?.length) {
if (!localTags?.length) {
filterCols.forEach((filter) => {
resetOptions(filter);
cleanOldTags(filter.title);
});
} else {
filterCols.forEach((filter) => {
if (filter.id === 'buildDuration') {
setTimeFilter(filter, $columns);
} else if (filter.id.includes('size')) {
} else if (filter.id.toLocaleLowerCase().includes('size')) {
setSizeFilter(filter, $columns);
} else if (filter.id === 'statusCode') {
setStatusCodeFilter(filter, $columns);
@@ -37,26 +38,25 @@ export function setFiltersOnNavigate(
export function setFilterData(filter: FilterData) {
const tagData = get(tags).find((tag) => tag.tag.includes(`**${filter.title}**`));
if (tagData) {
cleanOldTags(filter.title);
filter.tag = tagData.tag;
if (Array.isArray(tagData.value) && tagData.value?.length) {
const values = tagData.value as string[];
filter.options.forEach((option) => {
option.checked = values.includes(option.value);
});
}
cleanOldTags(filter.title);
const newTag = {
tag: tagData.tag.replace(',', ' or '),
value: tagData.value
};
parsedTags.update((tags) => {
tags.push(newTag);
return tags;
});
} else {
filter.tag = null;
resetOptions(filter);
cleanOldTags(filter.title);
}
}
@@ -64,7 +64,6 @@ export function setTimeFilter(filter: FilterData, columns: Column[]) {
const col = columns.find((c) => c.id === filter.id);
const timeTag = get(tags).find((tag) => tag.tag.includes(`**${filter.title}**`));
if (timeTag) {
cleanOldTags(filter.title);
const now = new Date();
const diff = now.getTime() - new Date(timeTag.value as string).getTime();
@@ -80,15 +79,16 @@ export function setTimeFilter(filter: FilterData, columns: Column[]) {
tag: `**${filter.title}** is **${dateRange.label}**`,
value: timeTag.value
};
filter.tag = `**${filter.title}** is **${dateRange.label}**`;
filter = filter;
cleanOldTags(filter.title);
parsedTags.update((tags) => {
tags.push(newTag);
return tags;
});
}
} else {
filter.tag = null;
cleanOldTags(filter.title);
}
}
@@ -106,11 +106,19 @@ export function setSizeFilter(filter: FilterData, columns: Column[]) {
return prev;
});
if (sizeRange) {
filter.tag = `**${filter.title}** is **${sizeRange.label}**`;
filter = filter;
cleanOldTags(filter.title);
const newTag = {
tag: `**${filter.title}** is **${sizeRange.label}**`,
value: sizeTag.value
};
parsedTags.update((tags) => {
tags.push(newTag);
return tags;
});
}
} else {
filter.tag = null;
cleanOldTags(filter.title);
}
}
@@ -123,11 +131,18 @@ export function setStatusCodeFilter(filter: FilterData, columns: Column[]) {
const codeRange = ranges.find((c) => c?.value && c.value === statusCodeTag.value);
if (codeRange) {
filter.tag = `**${filter.title}** is **${codeRange.label}**`;
filter = filter;
cleanOldTags(filter.title);
const newTag = {
tag: `**${filter.title}** is **${codeRange.label}**`,
value: statusCodeTag.value
};
parsedTags.update((tags) => {
tags.push(newTag);
return tags;
});
}
} else {
filter.tag = null;
cleanOldTags(filter.title);
}
}
@@ -135,7 +150,6 @@ export function setDateFilter(filter: FilterData, columns: Column[]) {
const dateTag = get(tags).find((tag) => tag.tag.includes(`**${filter.title}**`));
const col = columns.find((c) => c.id === filter.id);
if (dateTag) {
cleanOldTags(filter.title);
const now = new Date();
const diff = now.getTime() - new Date(dateTag.value as string).getTime();
@@ -147,6 +161,7 @@ export function setDateFilter(filter: FilterData, columns: Column[]) {
return prev;
});
if (dateRange) {
cleanOldTags(filter.title);
const newTag = {
tag: `**${filter.title}** is **${dateRange.label}**`,
value: dateTag.value
@@ -157,7 +172,7 @@ export function setDateFilter(filter: FilterData, columns: Column[]) {
});
}
} else {
filter.tag = null;
cleanOldTags(filter.title);
}
}
@@ -167,3 +182,9 @@ function cleanOldTags(title: string) {
return tags;
});
}
export function resetOptions(filter: FilterData) {
filter.options.forEach((option) => {
option.checked = false;
});
}
+5 -1
View File
@@ -20,7 +20,11 @@
environment variables are configured.
</p>
<div>
<Button compact external href="#/">Learn more</Button>
<Button
compact
external
href="https://appwrite.io/docs/advanced/self-hosting/functions#git"
>Learn more</Button>
</div>
</Layout.Stack>
</Alert.Inline>
+4 -3
View File
@@ -30,11 +30,13 @@
export let installationList = $installations;
export let product: 'functions' | 'sites' = 'functions';
let search = '';
let selectedInstallation = null;
$: {
hasInstallations = installationList?.total > 0;
}
let selectedInstallation = null;
async function loadInstallations() {
if (installationList) {
if (installationList.installations.length) {
@@ -56,7 +58,6 @@
}
}
let search = '';
async function loadRepositories(installationId: string, search: string) {
if (
!$repositories ||
@@ -91,7 +92,7 @@
$repository = $repositories.repositories[0];
}
return $repositories.repositories.slice(0, 4);
return $repositories.repositories;
}
</script>
+2
View File
@@ -0,0 +1,2 @@
export { default as Menu } from './menu.svelte';
export { default as SubMenu } from './subMenu.svelte';
@@ -1,8 +1,5 @@
<script lang="ts">
import { Button } from '$lib/elements/forms';
import { Card, Icon } from '@appwrite.io/pink-svelte';
import { tags } from './store';
import { IconFilterLine } from '@appwrite.io/pink-icons-svelte';
import { Card } from '@appwrite.io/pink-svelte';
import { createMenubar, melt } from '@melt-ui/svelte';
const {
@@ -11,30 +8,31 @@
} = createMenubar();
const {
elements: { trigger: trigger, menu: menu, separator: separator }
elements: { trigger: trigger, menu: menu, separator: separator },
states: { open }
} = createMenu();
function toggle() {
open.update((state) => !state);
}
</script>
<div use:melt={$menubar}>
<div use:melt={$trigger}>
{#if $tags.length}
<Button secondary badge={`${$tags.length}`}>
<Icon icon={IconFilterLine} slot="start" size="s" />
Filters
</Button>
{:else}
<Button secondary>
<Icon icon={IconFilterLine} slot="start" size="s" />
Filters
</Button>
{/if}
<slot {toggle} />
</div>
<div class="menu" use:melt={$menu}>
<Card.Base padding="xxxs">
<slot />
<div class="separator" use:melt={$separator} />
<slot name="end" />
{#if $$slots.start}
<slot name="start" />
<div class="separator" use:melt={$separator} />
{/if}
<slot name="menu" {toggle} />
{#if $$slots.end}
<div class="separator" use:melt={$separator} />
<slot name="end" />
{/if}
</Card.Base>
</div>
</div>
@@ -42,6 +40,7 @@
<style>
.menu {
min-width: 244px;
width: max-content;
z-index: 20;
}
+43
View File
@@ -0,0 +1,43 @@
<script lang="ts">
import { Card } from '@appwrite.io/pink-svelte';
import { createMenubar, melt } from '@melt-ui/svelte';
const {
builders: { createMenu }
} = createMenubar();
const {
elements: { separator: separator },
builders: { createSubmenu: createSubmenu }
} = createMenu();
const {
elements: { subMenu: subMenu, subTrigger: subTrigger }
} = createSubmenu();
</script>
<div use:melt={$subTrigger}>
<slot />
</div>
<div class="subMenu" use:melt={$subMenu}>
<Card.Base padding="xxxs">
{#if $$slots.start}
<slot name="start" />
<div class="separator" use:melt={$separator} />
{/if}
<slot name="menu" />
{#if $$slots.end}
<div class="separator" use:melt={$separator} />
<slot name="end" />
{/if}
</Card.Base>
</div>
<style>
.subMenu {
min-width: 244px;
margin-inline: -4px;
margin-block: -4px;
}
</style>
+1 -1
View File
@@ -16,7 +16,7 @@
<Typography.Text size="s" truncate color="--fgcolor-neutral-primary"
>{value}</Typography.Text>
{:else}
<Skeleton variant="line" width={100} height={19.5} />
<Skeleton variant="line" width="100%" height={19.5} />
{/if}
</slot>
</Layout.Stack>
+8
View File
@@ -0,0 +1,8 @@
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Flag } from '@appwrite.io/console';
import { sdk } from '$lib/stores/sdk';
export function getFlagUrl(countryCode: string) {
if (!isValueOfStringEnum(Flag, countryCode)) return '';
return sdk.forProject.avatars.getFlag(countryCode, 22, 15, 100)?.toString();
}
+89
View File
@@ -0,0 +1,89 @@
<script lang="ts">
import { Layout, Typography, Input, Tag, Icon, Button } from '@appwrite.io/pink-svelte';
import { IconPencil } from '@appwrite.io/pink-icons-svelte';
import { CustomId } from '$lib/components/index.js';
import type { Region } from '$lib/sdk/billing';
import { getFlagUrl } from '$lib/helpers/flag';
import { isCloud } from '$lib/system.js';
export let projectName: string;
export let id: string;
export let regions: Array<Region> = [];
export let region: string;
export let showTitle = true;
export let createProject: () => Promise<void>;
let showCustomId = false;
function getRegions() {
return regions
.filter((region) => region.$id !== 'default')
.sort((regionA, regionB) => {
if (regionA.disabled && !regionB.disabled) {
return 1;
}
return regionA.name > regionB.name ? 1 : -1;
})
.map((region) => {
return {
label: region.name,
value: region.$id,
leadingHtml: `<img src='${getFlagUrl(region.flag)}' alt='Region flag'/>`,
disabled: region.disabled
};
});
}
</script>
<svelte:head>
{#each regions as region}
<link rel="preload" as="image" href={getFlagUrl(region.flag)} />
{/each}
</svelte:head>
<form>
<Layout.Stack direction="column" gap="xxl">
{#if showTitle}
<Typography.Title size="l">Create your project</Typography.Title>
{/if}
<Layout.Stack direction="column" gap="xxl">
<Layout.Stack direction="column" gap="xxl">
<Layout.Stack direction="column" gap="s">
<Input.Text
label="Name"
placeholder="Project name"
required
bind:value={projectName} />
{#if !showCustomId}
<div>
<Tag
size="s"
on:click={() => {
showCustomId = true;
}}><Icon icon={IconPencil} /> Project ID</Tag>
</div>
{/if}
<CustomId
bind:show={showCustomId}
name="Project"
isProject
bind:id
fullWidth={true} />
</Layout.Stack>
{#if isCloud && regions.length > 0}
<Layout.Stack gap="xs"
><Input.Select
bind:value={region}
placeholder="Select a region"
options={getRegions()}
label="Region" />
<Typography.Text>Region cannot be changed after creation</Typography.Text>
</Layout.Stack>
{/if}
</Layout.Stack>
</Layout.Stack>
<Layout.Stack direction="row" justifyContent="flex-end"
><Button.Button type="button" variant="primary" size="s" on:click={createProject}>
Create</Button.Button>
</Layout.Stack>
</Layout.Stack>
</form>
@@ -1,12 +1,8 @@
<script lang="ts">
import { Card, Layout, Typography, Input, Tag, Icon, Button } from '@appwrite.io/pink-svelte';
import { IconPencil } from '@appwrite.io/pink-icons-svelte';
import { CustomId } from '$lib/components/index.js';
import { Card } from '@appwrite.io/pink-svelte';
import type { RegionList } from '$lib/sdk/billing';
import { onMount } from 'svelte';
import { isCloud } from '$lib/system';
import { sdk } from '$lib/stores/sdk';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Flag, ID, Region } from '@appwrite.io/console';
import Loading from './loading.svelte';
import { BillingPlan, Dependencies } from '$lib/constants';
@@ -15,27 +11,15 @@
import { base } from '$app/paths';
import { addNotification } from '$lib/stores/notifications';
import { tierToPlan } from '$lib/stores/billing';
import CreateProject from '$lib/layout/createProject.svelte';
let showCustomId = false;
let isLoading = false;
let id: string;
let startAnimation = false;
let projectName = '';
let region = Region.Default;
export let data: { regions: RegionList | null };
onMount(() => {
if (isCloud) {
if (data.regions) {
data.regions.regions.forEach((region) => fetch(getFlagUrl(region.flag)));
}
}
});
function getFlagUrl(countryCode: string) {
if (!isValueOfStringEnum(Flag, countryCode)) return '';
return sdk.forProject.avatars.getFlag(countryCode, 22, 15, 100)?.toString();
}
async function createProject() {
isLoading = true;
@@ -72,7 +56,7 @@
id ?? ID.unique(),
projectName,
teamId,
Region.Default
region
);
trackEvent(Submit.ProjectCreate, {
customId: !!id,
@@ -99,28 +83,6 @@
}
}
}
function getRegions() {
if (!data.regions) {
return;
}
return data.regions.regions
.filter((region) => region.$id !== 'default')
.sort((regionA, regionB) => {
if (regionA.disabled && !regionB.disabled) {
return 1;
}
return regionA.name > regionB.name ? 1 : -1;
})
.map((region) => {
return {
label: region.name,
value: region.$id,
leadingHtml: `<img src='${getFlagUrl(region.flag)}' alt='Region flag'/>`,
disabled: region.disabled
};
});
}
</script>
<svelte:head>
@@ -144,57 +106,13 @@
height="22"
class="u-only-dark"
alt="Appwrite Logo" />
<Card.Base variant="primary" padding="l"
><form>
<Layout.Stack direction="column" gap="xxl">
<Typography.Title size="l">Create your project</Typography.Title>
<Layout.Stack direction="column" gap="xxl">
<Layout.Stack direction="column" gap="xxl">
<Layout.Stack direction="column" gap="s">
<Input.Text
label="Name"
placeholder="Project name"
required
bind:value={projectName} />
{#if !showCustomId}
<div>
<Tag
size="s"
on:click={() => {
showCustomId = true;
}}><Icon icon={IconPencil} /> Project ID</Tag>
</div>
{/if}
<CustomId
bind:show={showCustomId}
name="Project"
isProject
bind:id
fullWidth={true} />
</Layout.Stack>
{#if data.regions}
<Layout.Stack gap="xs"
><Input.Select
placeholder="Select a region"
options={getRegions()}
label="Region" />
<Typography.Text
>Region cannot be changed after creation</Typography.Text>
</Layout.Stack>
{/if}
</Layout.Stack>
</Layout.Stack>
<Layout.Stack direction="row" justifyContent="flex-end"
><Button.Button
type="button"
variant="primary"
size="s"
on:click={createProject}>
Create</Button.Button>
</Layout.Stack>
</Layout.Stack>
</form></Card.Base>
<Card.Base variant="primary" padding="l">
<CreateProject
regions={data.regions.regions}
bind:projectName
bind:id
bind:region
{createProject} /></Card.Base>
{/if}
</div>
@@ -41,11 +41,13 @@
IconUnity
} from '@appwrite.io/pink-icons-svelte';
import { getPlatformInfo } from '$lib/helpers/platform';
import CreateProjectCloud from './createProjectCloud.svelte';
export let data;
let addOrganization = false;
let showCreate = false;
let showCreateProjectCloud = false;
function allServiceDisabled(project: Models.Project): boolean {
let disabled = true;
@@ -66,7 +68,7 @@
function handleCreateProject() {
if (!$canWriteProjects) return;
if (isCloud) wizard.start(Create);
if (isCloud) showCreateProjectCloud = true;
else showCreate = true;
}
@@ -234,3 +236,6 @@
<CreateOrganization bind:show={addOrganization} />
<CreateProject bind:show={showCreate} teamId={$page.params.organization} />
{#if showCreateProjectCloud}
<CreateProjectCloud bind:showCreateProjectCloud regions={regions.regions} />
{/if}
@@ -1,21 +1,22 @@
<script lang="ts">
import { WizardWithSteps } from '$lib/layout';
import { sdk } from '$lib/stores/sdk';
import { onDestroy } from 'svelte';
import { addNotification } from '$lib/stores/notifications';
import Step1 from './wizard/step1.svelte';
import Step2 from './wizard/step2.svelte';
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
import { goto, invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
import { page } from '$app/stores';
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
import { ID, Region } from '@appwrite.io/console';
import { ID, Region as ConsoleRegion } from '@appwrite.io/console';
import { createProject } from './wizard/store';
import { wizard } from '$lib/stores/wizard';
import { base } from '$app/paths';
import CreateProject from '$lib/layout/createProject.svelte';
import { Modal } from '$lib/components';
import type { Region } from '$lib/sdk/billing';
const teamId = $page.params.organization;
export let regions: Array<Region> = [];
export let showCreateProjectCloud: boolean;
async function onFinish() {
await invalidate(Dependencies.FUNCTIONS);
@@ -28,7 +29,7 @@
$createProject?.id ?? ID.unique(),
$createProject.name,
teamId,
$createProject.region as Region
$createProject.region as ConsoleRegion
);
trackEvent(Submit.ProjectCreate, {
customId: !!$createProject?.id,
@@ -39,8 +40,8 @@
type: 'success',
message: `${$createProject.name} has been created`
});
await onFinish();
await goto(`${base}/project-${project.$id}`);
wizard.hide();
} catch (e) {
addNotification({
type: 'error',
@@ -57,20 +58,13 @@
region: 'fra'
};
});
const stepsComponents: WizardStepsType = new Map();
stepsComponents.set(1, {
label: 'Details',
component: Step1
});
stepsComponents.set(2, {
label: 'Region',
component: Step2
});
</script>
<WizardWithSteps
title="Create project"
steps={stepsComponents}
finalMethod={create}
on:exit={onFinish} />
<Modal bind:show={showCreateProjectCloud} title={'Create Project'}
><CreateProject
createProject={create}
showTitle={false}
bind:id={$createProject.id}
bind:projectName={$createProject.name}
bind:region={$createProject.region}
{regions} /></Modal>
@@ -1,42 +0,0 @@
<script lang="ts">
import { CustomId } from '$lib/components';
import { Pill } from '$lib/elements';
import { InputText, FormList } from '$lib/elements/forms';
import { WizardStep } from '$lib/layout';
import { sdk } from '$lib/stores/sdk';
import { createProject, regions } from './store';
let showCustomId = false;
sdk.forConsole.billing.listRegions().then(regions.set);
</script>
<WizardStep>
<svelte:fragment slot="title">Details</svelte:fragment>
<FormList>
<InputText
label="Name"
id="name"
placeholder="Project name"
bind:value={$createProject.name}
autofocus
required />
{#if !showCustomId}
<div>
<Pill button on:click={() => (showCustomId = !showCustomId)}>
<span class="icon-pencil" aria-hidden="true" />
<span class="text">Project ID </span>
</Pill>
</div>
{:else}
<CustomId
autofocus
bind:show={showCustomId}
name="Project"
isProject={true}
bind:id={$createProject.id}
fullWidth />
{/if}
</FormList>
</WizardStep>
@@ -1,133 +0,0 @@
<script lang="ts">
import { RegionCard } from '$lib/components';
import { Flag, Pill } from '$lib/elements';
import { WizardStep } from '$lib/layout';
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { createProject, regions } from './store';
import type { Region } from '$lib/sdk/billing';
import { addNotification } from '$lib/stores/notifications';
import type { Models } from '@appwrite.io/console';
import { page } from '$app/stores';
let prefs: Models.Preferences;
onMount(async () => {
prefs = $page.data.account.prefs;
});
async function notifyRegion(selectedRegion: Region) {
try {
let newPrefs = { ...prefs };
newPrefs.notifications ??= [];
newPrefs.notifications = [...newPrefs.notifications, selectedRegion.$id];
const response = await sdk.forConsole.account.updatePrefs(newPrefs);
prefs = response.prefs;
addNotification({
type: 'success',
isHtml: true,
message: `You will be notified via email when <b>${selectedRegion.name}</b> region is available`
});
} catch (error) {
addNotification({
type: 'error',
message: 'Something went wrong, please try again later'
});
}
}
async function unNotifyRegion(selectedRegion: Region) {
try {
let newPrefs = { ...prefs };
newPrefs.notifications = newPrefs.notifications ?? [];
newPrefs.notifications = [...newPrefs.notifications, selectedRegion.$id];
newPrefs.notifications = newPrefs.notifications.filter(
(region: string) => region !== selectedRegion.$id
);
const response = await sdk.forConsole.account.updatePrefs(newPrefs);
prefs = response.prefs;
addNotification({
type: 'info',
isHtml: true,
message: `You will no longer be notified when the <b>${selectedRegion.name}</b> region is available`
});
} catch (error) {
addNotification({
type: 'error',
message: 'Something went wrong, please try again later'
});
}
}
$: notifications = prefs?.notifications ?? [];
</script>
<WizardStep>
<svelte:fragment slot="title">Regions</svelte:fragment>
<svelte:fragment slot="subtitle">
Choose a deployment region for your project. This region cannot be changed.
</svelte:fragment>
{#if $regions}
<ul
class="grid-box u-margin-block-start-16"
style="--p-grid-item-size:12em; --p-grid-item-size-small-screens:12rem; --grid-gap: 1rem;">
{#each $regions.regions
.filter((r) => r.$id !== 'default')
.sort((regionA, regionB) => {
if (regionA.disabled >= regionB.disabled) {
return 1;
}
return -1;
}) as region}
<li>
<RegionCard
name="region"
bind:group={$createProject.region}
value={region.$id}
disabled={region.disabled}>
<div
class="u-flex u-flex-vertical u-gap-8 u-justify-main-center u-cross-center u-margin-inline-auto">
{#if region.disabled}
<Flag
width={40}
height={30}
class={region.disabled ? 'u-opacity-50' : ''}
flag={region.flag}
name={region.name} />
<p class:u-opacity-50={region.disabled}>{region.name}</p>
{#if !notifications.includes(region.$id)}
<Pill
button
event="region_notify"
on:click={() => {
notifyRegion(region);
}}>
<span class="icon-bell" aria-hidden="true" />
<span class="text">Notify me</span>
</Pill>
{:else}
<Pill
selected
button
event="region_notify"
on:click={() => {
unNotifyRegion(region);
}}>
<span class="icon-check" aria-hidden="true" />
<span class="text">Notify me</span>
</Pill>
{/if}
{:else}
<Flag
width={40}
height={30}
flag={region.flag}
name={region.name} />
{region.name}
{/if}
</div>
</RegionCard>
</li>
{/each}
</ul>
{/if}
</WizardStep>
@@ -10,6 +10,7 @@
import { installation, repository } from '$lib/stores/vcs';
import { Repositories } from '$lib/components/git';
import {
Alert,
Avatar,
Badge,
Card,
@@ -21,6 +22,7 @@
import { IconArrowSmRight } from '@appwrite.io/pink-icons-svelte';
import Wizard from '$lib/layout/wizard.svelte';
import { Link } from '$lib/elements';
import { Button } from '$lib/elements/forms';
export let data;
@@ -62,16 +64,35 @@
`${wizardBase}/create-function/repository-${e.detail.id}?installation=${$installation.$id}`
);
}
$: console.log(data);
</script>
<Wizard title="Create function" href={previousPage} column columnSize="l">
<div class="git-container u-position-relative">
<Layout.Stack gap="l">
<!-- TODO: fix mobile -->
<Layout.GridFraction start={4} end={6} gap="l" rowSize="auto">
<Card.Base>
<Layout.Stack gap="l">
<Layout.GridFraction start={4} end={6} gap="l" rowSize="auto">
<Card.Base>
{#if isSelfHosted && !isVcsEnabled}
<Layout.Stack gap="xl">
<Typography.Title size="s">Connect Git repository</Typography.Title>
<Alert.Inline
status="info"
title="Connect your self-hosted instance to Git ">
<Layout.Stack>
<p>
Configure your self-hosted instance to connect your function to
a Git repository.
</p>
<div>
<Button
compact
external
href="https://appwrite.io/docs/advanced/self-hosting/functions#git"
>Learn more</Button>
</div>
</Layout.Stack>
</Alert.Inline>
</Layout.Stack>
{:else}
<Layout.Stack
gap="xl"
justifyContent="space-between"
@@ -103,145 +124,106 @@
</Layout.Stack>
{/if}
</Layout.Stack>
</Card.Base>
{/if}
</Card.Base>
<Card.Base>
<Layout.Stack gap="xl">
<Typography.Title size="s">Quick start</Typography.Title>
<Layout.Grid columnsXXS={1} columnsXS={2} columnsS={3} columns={4} gap="s">
{#each starterTemplateRuntimes.slice(0, 6) as template}
{@const iconName = template.name.split('-')[0]}
{@const runtimeDetail = baseRuntimesList.find(
(runtime) => runtime.$id === template.name
)}
<Card.Link
variant="secondary"
radius="s"
padding="s"
on:click={() => {
trackEvent('click_connect_template', {
from: 'cover',
template: starterTemplate.id,
runtime: template.name
});
}}
href={`${wizardBase}/create-function/template-${starterTemplate.id}?runtime=${runtimeDetail.$id}`}>
<Layout.Stack direction="row" gap="s" alignItems="center">
<Avatar
size="xs"
alt={template.name}
empty={!template.name}>
<SvgIcon name={iconName} iconSize="small" />
</Avatar>
<Typography.Text color="--fgcolor-neutral-primary">
<Layout.Stack
direction="row"
gap="xs"
alignItems="center">
{runtimeDetail?.name}
{#if runtimeDetail?.name?.toLowerCase() === 'deno'}
<Badge
variant="secondary"
size="xs"
content="New" />
{/if}
</Layout.Stack>
</Typography.Text>
</Layout.Stack>
</Card.Link>
{/each}
</Layout.Grid>
<Divider />
<Layout.Grid columnsS={1} columns={2}>
{#each featuredTemplatesList as template}
<Card.Link
radius="s"
padding="xs"
href={`${wizardBase}/create-function/template-${template.id}`}
on:click={() => {
trackEvent('click_connect_template', {
from: 'cover',
template: template.name
});
}}>
<Layout.Stack gap="xxs">
<Layout.Stack
direction="row"
gap="s"
alignItems="center"
justifyContent="space-between">
<Typography.Text
variant="m-500"
color="--fgcolor-neutral-primary">
{template.name}
</Typography.Text>
<Icon
icon={IconArrowSmRight}
color="--fgcolor-neutral-tertiary" />
<Card.Base>
<Layout.Stack gap="xl">
<Typography.Title size="s">Quick start</Typography.Title>
<Layout.Grid columnsXXS={1} columnsXS={2} columnsS={3} columns={4} gap="s">
{#each starterTemplateRuntimes.slice(0, 6) as template}
{@const iconName = template.name.split('-')[0]}
{@const runtimeDetail = baseRuntimesList.find(
(runtime) => runtime.$id === template.name
)}
<Card.Link
variant="secondary"
radius="s"
padding="s"
on:click={() => {
trackEvent('click_connect_template', {
from: 'cover',
template: starterTemplate.id,
runtime: template.name
});
}}
href={`${wizardBase}/create-function/template-${starterTemplate.id}?runtime=${runtimeDetail.$id}`}>
<Layout.Stack direction="row" gap="s" alignItems="center">
<Avatar size="xs" alt={template.name} empty={!template.name}>
<SvgIcon name={iconName} iconSize="small" />
</Avatar>
<Typography.Text color="--fgcolor-neutral-primary">
<Layout.Stack direction="row" gap="xs" alignItems="center">
{runtimeDetail?.name}
{#if runtimeDetail?.name?.toLowerCase() === 'deno'}
<Badge
variant="secondary"
size="xs"
content="New" />
{/if}
</Layout.Stack>
<Typography.Text variant="m-400">
{template.tagline}
</Typography.Text>
</Layout.Stack>
</Card.Link>
{/each}
</Layout.Grid>
<Divider />
<Layout.Grid columnsS={1} columns={2}>
{#each featuredTemplatesList as template}
<Card.Link
radius="s"
padding="xs"
href={`${wizardBase}/create-function/template-${template.id}`}
on:click={() => {
trackEvent('click_connect_template', {
from: 'cover',
template: template.name
});
}}>
<Layout.Stack gap="xxs">
<Layout.Stack
direction="row"
gap="s"
alignItems="center"
justifyContent="space-between">
<Typography.Text
variant="m-500"
color="--fgcolor-neutral-primary">
{template.name}
</Typography.Text>
<Icon
icon={IconArrowSmRight}
color="--fgcolor-neutral-tertiary" />
</Layout.Stack>
</Card.Link>
{/each}
</Layout.Grid>
<Typography.Text variant="m-400">
{template.tagline}
</Typography.Text>
</Layout.Stack>
</Card.Link>
{/each}
</Layout.Grid>
<Link variant="quiet" href={`${wizardBase}/templates`}>
<Layout.Stack direction="row" gap="xs">
Browse all templates <Icon icon={IconArrowSmRight} />
</Layout.Stack>
</Link>
</Layout.Stack>
</Card.Base>
</Layout.GridFraction>
<Link variant="quiet" href={`${wizardBase}/templates`}>
<Layout.Stack direction="row" gap="xs">
Browse all templates <Icon icon={IconArrowSmRight} />
</Layout.Stack>
</Link>
</Layout.Stack>
</Card.Base>
</Layout.GridFraction>
<span>
You can also create a function <Link
on:click={() => {
trackEvent('click_create_function_manual', { from: 'cover' });
}}
href={`${wizardBase}/create-function/manual`}>manually</Link>
or using the CLI.
<Link href="https://appwrite.io/docs/products/functions/deployment" external
>Learn more</Link
>.
</span>
</Layout.Stack>
{#if isSelfHosted && !isVcsEnabled}
<div
class="overlay u-flex-vertical u-position-absolute u-height-100-percent u-width-full-line u-z-index-1 u-text-center u-inset-0"
style="border-radius: var(--border-radius-medium); ">
<div
class="u-flex-vertical u-height-100-percent u-main-center u-cross-center u-gap-16 u-padding-inline-24">
<Typography.Title size="s" align="center">
Connect your self-hosted instance to Git
</Typography.Title>
<p style="max-width: 420px">
Configure your self-hosted instance to connect your function to a Git
repository.
<a
href="https://appwrite.io/docs/advanced/self-hosting/functions#git"
target="_blank"
rel="noopener noreferrer"
class="link">Learn more</a
>.
</p>
</div>
</div>
{/if}
</div>
<span>
You can also create a function <Link
on:click={() => {
trackEvent('click_create_function_manual', { from: 'cover' });
}}
href={`${wizardBase}/create-function/manual`}>manually</Link>
or using the CLI.
<Link href="https://appwrite.io/docs/products/functions/deployment" external
>Learn more</Link
>.
</span>
</Layout.Stack>
</Wizard>
<style lang="scss">
.git-container .overlay {
background: linear-gradient(
0,
var(--bgcolor-neutral-primary) 50%,
color-mix(in srgb, var(--bgcolor-neutral-primary) 90%, transparent) 70%,
color-mix(in srgb, var(--bgcolor-neutral-primary) 20%, transparent) 90%
);
}
</style>
@@ -19,7 +19,7 @@ export const load: LayoutLoad = async ({ params, depends }) => {
Query.equal('type', RuleType.DEPLOYMENT),
Query.equal('deploymentResourceType', DeploymentResourceType.FUNCTION),
Query.equal('deploymentResourceId', params.function),
Query.equal('deploymentId', func.deployment),
Query.equal('deploymentId', func.deploymentId),
Query.limit(1)
]);
@@ -31,7 +31,7 @@
} from '@appwrite.io/pink-icons-svelte';
import { app } from '$lib/stores/app';
import CreateActionMenu from './(components)/createActionMenu.svelte';
import { QuickFilters } from '$lib/components/filters';
import { ParsedTagList, QuickFilters } from '$lib/components/filters';
export let data;
@@ -171,23 +171,26 @@
{/if}
<Layout.Stack gap="l">
<Layout.Stack direction="row" alignItems="center">
<Layout.Stack direction="row" gap="s" wrap="wrap">
{#if data.deploymentList.total}
<QuickFilters {columns} analyticsSource="function_deployments" />
{/if}
</Layout.Stack>
<Layout.Stack direction="row" gap="s" inline>
{#if data.deploymentList.total}
<ViewSelector view={View.Table} {columns} hideView />
{/if}
<CreateActionMenu let:toggle>
<Button on:click={toggle} event="create_deployment">
<Icon icon={IconPlus} size="s" slot="start" />
Create deployment
</Button>
</CreateActionMenu>
<Layout.Stack>
<Layout.Stack direction="row" alignItems="center">
<Layout.Stack direction="row" gap="s" wrap="wrap">
{#if data.deploymentList.total}
<QuickFilters {columns} analyticsSource="function_deployments" />
{/if}
</Layout.Stack>
<Layout.Stack direction="row" gap="s" inline>
{#if data.deploymentList.total}
<ViewSelector view={View.Table} {columns} hideView />
{/if}
<CreateActionMenu let:toggle>
<Button on:click={toggle} event="create_deployment">
<Icon icon={IconPlus} size="s" slot="start" />
Create deployment
</Button>
</CreateActionMenu>
</Layout.Stack>
</Layout.Stack>
<ParsedTagList />
</Layout.Stack>
{#if data.deploymentList.total}
@@ -96,7 +96,7 @@
<Button secondary href={getDownload(data.deployment.$id)}>Download</Button>
{/if}
{#if data.func.deployment !== data.deployment.$id && data.deployment.status === 'ready'}
{#if data.func.deploymentId !== data.deployment.$id && data.deployment.status === 'ready'}
<Button
disabled={data.activeDeployment}
on:click={() => {
@@ -14,6 +14,6 @@ export const load: PageLoad = async ({ params, depends, parent }) => {
return {
func,
deployment,
activeDeployment: func.deployment === params.deployment
activeDeployment: func.deploymentId === params.deployment
};
};
@@ -13,7 +13,7 @@
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import Table from './table.svelte';
import { columns } from './store';
import { QuickFilters } from '$lib/components/filters';
import { ParsedTagList, QuickFilters } from '$lib/components/filters';
export let data;
@@ -29,21 +29,24 @@
</script>
<Container>
<Layout.Stack direction="row" alignItems="center" justifyContent="space-between">
<QuickFilters {columns} analyticsSource="function_executions" />
<Layout.Stack>
<Layout.Stack direction="row" alignItems="center" justifyContent="space-between">
<QuickFilters {columns} analyticsSource="function_executions" />
<Layout.Stack gap="s" inline direction="row" alignItems="center">
{#if data?.executions?.total}
<ViewSelector view={View.Table} {columns} hideView />
{/if}
<Button
event="execute_function"
href={`${base}/project-${$project.$id}/functions/function-${data.func.$id}/executions/execute-function`}
disabled={!data.func.$id || !data.func?.deployment}>
<Icon icon={IconPlus} size="s" slot="start" />
Create execution
</Button>
<Layout.Stack gap="s" inline direction="row" alignItems="center">
{#if data?.executions?.total}
<ViewSelector view={View.Table} {columns} hideView />
{/if}
<Button
event="execute_function"
href={`${base}/project-${$project.$id}/functions/function-${data.func.$id}/executions/execute-function`}
disabled={!data.func.$id || !data.func?.deploymentId}>
<Icon icon={IconPlus} size="s" slot="start" />
Create execution
</Button>
</Layout.Stack>
</Layout.Stack>
<ParsedTagList />
</Layout.Stack>
{#if !data.func.logging}
@@ -1,7 +1,6 @@
import { sdk } from '$lib/stores/sdk';
import { Dependencies } from '$lib/constants';
import type { PageLoad } from './$types';
import { proxyRuleList } from '../../store';
export const load: PageLoad = async ({ params, depends, parent }) => {
const data = await parent();
@@ -136,7 +136,7 @@
}}>
Redeploy
</ActionMenu.Item.Button>
{#if deployment.status === 'ready' && deployment.$id !== $func.deployment}
{#if deployment.status === 'ready' && deployment.$id !== $func.deploymentId}
<ActionMenu.Item.Button
trailingIcon={IconLightningBolt}
on:click={(e) => {
@@ -12,8 +12,8 @@
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
import { roles } from '$lib/stores/billing';
import InputSelect from '$lib/elements/forms/inputSelect.svelte';
import Roles from '$lib/components/roles/roles.svelte';
import { Layout } from '@appwrite.io/pink-svelte';
import { isCloud, isSelfHosted } from '$lib/system';
export let show = false;
@@ -24,7 +24,7 @@
let email: string,
name: string,
error: string,
role: string = 'viewer';
role: string = isSelfHosted ? 'owner' : 'viewer';
async function create() {
try {
@@ -59,8 +59,6 @@
email = null;
name = null;
}
//TODO: fix popover
</script>
<Modal title="Add collaborator" {error} bind:show onSubmit={create}>
@@ -75,14 +73,15 @@
placeholder="Enter email"
autofocus={true}
bind:value={email} />
<InputSelect
popover={Roles}
id="role"
label="Role"
placeholder="Select role"
required
options={roles}
bind:value={role} />
{#if isCloud}
<InputSelect
id="role"
label="Role"
placeholder="Select role"
required
options={roles}
bind:value={role} />
{/if}
</Layout.Stack>
<svelte:fragment slot="footer">
<Button secondary on:click={() => (show = false)}>Cancel</Button>
@@ -1,5 +1,7 @@
<script lang="ts">
import { page } from '$app/stores';
import { Menu } from '$lib/components/menu';
import SubMenu from '$lib/components/menu/subMenu.svelte';
import { Button } from '$lib/elements/forms';
import { sdk } from '$lib/stores/sdk';
import { DeploymentDownloadType, type Models } from '@appwrite.io/console';
@@ -11,7 +13,7 @@
IconTrash,
IconXCircle
} from '@appwrite.io/pink-icons-svelte';
import { ActionMenu, Icon, Popover } from '@appwrite.io/pink-svelte';
import { ActionMenu, Icon } from '@appwrite.io/pink-svelte';
export let selectedDeployment: Models.Deployment;
export let deployment: Models.Deployment;
@@ -22,28 +24,31 @@
export let activeDeployment: string;
export let inCard = false;
function getDownload(deploymentId: string) {
function getOutputDownload(deploymentId: string) {
return (
sdk.forProject.sites.getDeploymentDownload($page.params.site, deploymentId).toString() +
'&mode=admin',
DeploymentDownloadType.Source
sdk.forProject.sites.getDeploymentDownload(
$page.params.site,
deploymentId.toString(),
DeploymentDownloadType.Output
) + '&mode=admin'
);
}
function getSourceDownload(deploymentId: string) {
return (
sdk.forProject.sites.getDeploymentDownload(
$page.params.site,
deploymentId.toString(),
DeploymentDownloadType.Source
) + '&mode=admin'
);
}
</script>
<Popover padding="none" placement="bottom-end" let:toggle>
<Button
text={!inCard}
secondary={inCard}
icon
size="s"
on:click={(e) => {
e.preventDefault();
toggle(e);
}}>
<Menu>
<Button text={!inCard} secondary={inCard} icon size="s">
<Icon size="s" icon={IconDotsHorizontal} />
</Button>
<svelte:fragment slot="tooltip" let:toggle>
<svelte:fragment slot="menu" let:toggle>
<ActionMenu.Root>
{#if !inCard}
<ActionMenu.Item.Button
@@ -52,7 +57,7 @@
e.preventDefault();
selectedDeployment = deployment;
showRedeploy = true;
toggle(e);
toggle();
}}>
Redeploy
</ActionMenu.Item.Button>
@@ -64,21 +69,35 @@
e.preventDefault();
selectedDeployment = deployment;
showActivate = true;
toggle(e);
toggle();
}}>
Activate
</ActionMenu.Item.Button>
{/if}
{#if deployment?.status === 'ready' || deployment?.status === 'failed'}
<ActionMenu.Item.Anchor
href={getDownload(deployment.$id)}
external
leadingIcon={IconDownload}
on:click={(e) => {
toggle(e);
}}>
Download
</ActionMenu.Item.Anchor>
<SubMenu>
<ActionMenu.Root noPadding>
<ActionMenu.Item.Button leadingIcon={IconDownload}>
Download
</ActionMenu.Item.Button>
</ActionMenu.Root>
<svelte:fragment slot="menu">
<ActionMenu.Root noPadding>
<ActionMenu.Item.Anchor
on:click={toggle}
href={getSourceDownload(deployment.$id)}
external>
Download source
</ActionMenu.Item.Anchor>
<ActionMenu.Item.Anchor
on:click={toggle}
href={getOutputDownload(deployment.$id)}
external>
Download output
</ActionMenu.Item.Anchor>
</ActionMenu.Root>
</svelte:fragment>
</SubMenu>
{/if}
{#if deployment?.status === 'processing' || deployment?.status === 'building' || deployment.status === 'waiting'}
@@ -89,7 +108,6 @@
e.preventDefault();
selectedDeployment = deployment;
showCancel = true;
toggle(e);
}}>
Cancel
</ActionMenu.Item.Button>
@@ -102,11 +120,10 @@
e.preventDefault();
selectedDeployment = deployment;
showDelete = true;
toggle(e);
}}>
Delete
</ActionMenu.Item.Button>
{/if}
</ActionMenu.Root>
</svelte:fragment>
</Popover>
</Menu>
@@ -18,7 +18,7 @@
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
import CreateCliModal from './createCliModal.svelte';
import { QuickFilters } from '$lib/components/filters';
import { ParsedTagList, QuickFilters } from '$lib/components/filters';
export let data;
@@ -45,54 +45,57 @@
<Layout.Stack gap="xxxl">
<DeploymentMetrics deploymentList={data.deploymentList} />
<Layout.Stack gap="l">
<Layout.Stack justifyContent="space-between" direction="row">
<Layout.Stack alignItems="center" direction="row">
{#if data.deploymentList.total || data?.query}
<QuickFilters {columns} analyticsSource="site_deployments" />
{/if}
</Layout.Stack>
<Layout.Stack>
<Layout.Stack justifyContent="space-between" direction="row">
<Layout.Stack alignItems="center" direction="row">
{#if data.deploymentList.total || data?.query}
<QuickFilters {columns} analyticsSource="site_deployments" />
{/if}
</Layout.Stack>
<Layout.Stack direction="row" inline>
{#if data.deploymentList.total}
<ViewSelector view={View.Table} {columns} hideView />
{/if}
<Popover padding="none" let:toggle>
<Button size="s" on:click={toggle}>
<Icon size="s" icon={IconPlus} />
Create deployment
</Button>
<svelte:fragment slot="tooltip" let:toggle>
<ActionMenu.Root>
<ActionMenu.Item.Button
badge="Recommended"
on:click={(e) => {
toggle(e);
if (!hasInstallation) {
showConnectRepo = true;
} else {
showCreateDeployment = true;
}
}}>
Git
</ActionMenu.Item.Button>
<ActionMenu.Item.Button
on:click={(e) => {
toggle(e);
showConnectCLI = true;
}}>
CLI
</ActionMenu.Item.Button>
<ActionMenu.Item.Button
on:click={(e) => {
toggle(e);
showConnectManual = true;
}}>
Manual
</ActionMenu.Item.Button>
</ActionMenu.Root>
</svelte:fragment>
</Popover>
<Layout.Stack direction="row" inline>
{#if data.deploymentList.total}
<ViewSelector view={View.Table} {columns} hideView />
{/if}
<Popover padding="none" let:toggle>
<Button size="s" on:click={toggle}>
<Icon size="s" icon={IconPlus} />
Create deployment
</Button>
<svelte:fragment slot="tooltip" let:toggle>
<ActionMenu.Root>
<ActionMenu.Item.Button
badge="Recommended"
on:click={(e) => {
toggle(e);
if (!hasInstallation) {
showConnectRepo = true;
} else {
showCreateDeployment = true;
}
}}>
Git
</ActionMenu.Item.Button>
<ActionMenu.Item.Button
on:click={(e) => {
toggle(e);
showConnectCLI = true;
}}>
CLI
</ActionMenu.Item.Button>
<ActionMenu.Item.Button
on:click={(e) => {
toggle(e);
showConnectManual = true;
}}>
Manual
</ActionMenu.Item.Button>
</ActionMenu.Root>
</svelte:fragment>
</Popover>
</Layout.Stack>
</Layout.Stack>
<ParsedTagList />
</Layout.Stack>
{#if data.deploymentList.total}
@@ -7,11 +7,10 @@
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import Table from './table.svelte';
import { QuickFilters } from '$lib/components/filters';
import { ParsedTagList, QuickFilters } from '$lib/components/filters';
import { Card, Empty, Layout } from '@appwrite.io/pink-svelte';
import { View } from '$lib/helpers/load';
import { columns } from './store';
import AppliedFilter from '$lib/components/filters/appliedFilter.svelte';
export let data;
@@ -35,7 +34,7 @@
{/if}
</Layout.Stack>
<AppliedFilter />
<ParsedTagList />
</Layout.Stack>
{#if data?.logs?.total}
<Table columns={$columns} logs={data.logs} />
@@ -27,7 +27,7 @@
site.buildCommand || undefined,
site.outputDirectory || undefined,
(site?.buildRuntime as BuildRuntime) || undefined,
site.adapter as Adapter,
(site?.adapter as Adapter) || undefined,
site.fallbackFile || undefined,
'',
'',