mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'feat-pink-v2' into 'add-date-tooltips'.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { LabelCard } from '$lib/components';
|
||||
import { consoleVariables } from '$routes/(console)/store';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
|
||||
const isVcsEnabled = $consoleVariables?._APP_VCS_ENABLED === true;
|
||||
export let connectBehaviour: 'now' | 'later' = isVcsEnabled ? 'now' : 'later';
|
||||
</script>
|
||||
|
||||
<Layout.Grid columns={2} columnsXS={1}>
|
||||
<LabelCard
|
||||
value="now"
|
||||
bind:group={connectBehaviour}
|
||||
disabled={!isVcsEnabled}
|
||||
title="Connect your repository">
|
||||
Clone this template into a new Git repository or link it to an existing one.
|
||||
</LabelCard>
|
||||
<LabelCard value="later" bind:group={connectBehaviour}>
|
||||
<svelte:fragment slot="title">Connect later</svelte:fragment>
|
||||
Deploy now and connect your version control later via CLI or Git integration in your settings.
|
||||
</LabelCard>
|
||||
</Layout.Grid>
|
||||
@@ -1,54 +1,48 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import Button from '$lib/elements/forms/button.svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { consoleVariables } from '$routes/(console)/store';
|
||||
import { IconGithub } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Card, Empty, Icon } from '@appwrite.io/pink-svelte';
|
||||
import Alert from '../alert.svelte';
|
||||
import { Alert, Card, Empty, Icon, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { isSelfHosted } from '$lib/system';
|
||||
import { connectGitHub } from '$lib/stores/git';
|
||||
|
||||
export let callbackState: Record<string, string> = null;
|
||||
|
||||
let isVcsEnabled = $consoleVariables?._APP_VCS_ENABLED === true;
|
||||
function connectGitHub() {
|
||||
const redirect = new URL($page.url);
|
||||
if (callbackState) {
|
||||
Object.keys(callbackState).forEach((key) => {
|
||||
redirect.searchParams.append(key, callbackState[key]);
|
||||
});
|
||||
}
|
||||
const target = new URL(`${sdk.forProject.client.config.endpoint}/vcs/github/authorize`);
|
||||
target.searchParams.set('project', $page.params.project);
|
||||
target.searchParams.set('success', redirect.toString());
|
||||
target.searchParams.set('failure', redirect.toString());
|
||||
target.searchParams.set('mode', 'admin');
|
||||
return target;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !isVcsEnabled && isSelfHosted}
|
||||
<Alert>
|
||||
<span slot="title"> Installing Git on a self-hosted instance </span>
|
||||
<p>
|
||||
Before installing Git in a locally hosted Appwrite project, ensure your environment
|
||||
variables are configured.
|
||||
</p>
|
||||
<svelte:fragment slot="buttons">
|
||||
<Button secondary external href="#/">Learn more</Button>
|
||||
</svelte:fragment>
|
||||
</Alert>
|
||||
{/if}
|
||||
<Card.Base padding="none" border="dashed">
|
||||
<Empty
|
||||
type="secondary"
|
||||
title="No installation was added to the project yet"
|
||||
description="Add an installation to connect repositories">
|
||||
<svelte:fragment slot="actions">
|
||||
<Button secondary href={connectGitHub().toString()} disabled={!isVcsEnabled}>
|
||||
<Icon slot="start" icon={IconGithub} />
|
||||
Connect to GitHub
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</Empty>
|
||||
</Card.Base>
|
||||
<Layout.Stack>
|
||||
{#if !isVcsEnabled && isSelfHosted}
|
||||
<Alert.Inline status="info" title="Installing Git on a self-hosted instance ">
|
||||
<Layout.Stack>
|
||||
<p>
|
||||
Before installing Git in a locally hosted Appwrite project, ensure your
|
||||
environment variables are configured.
|
||||
</p>
|
||||
<div>
|
||||
<Button
|
||||
compact
|
||||
external
|
||||
href="https://appwrite.io/docs/advanced/self-hosting/functions#git"
|
||||
>Learn more</Button>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
</Alert.Inline>
|
||||
{/if}
|
||||
<Card.Base padding="none" border="dashed">
|
||||
<Empty
|
||||
type="secondary"
|
||||
title="No installation was added to the project yet"
|
||||
description="Add an installation to connect repositories">
|
||||
<svelte:fragment slot="actions">
|
||||
<Button
|
||||
secondary
|
||||
href={connectGitHub(callbackState).toString()}
|
||||
disabled={!isVcsEnabled}>
|
||||
<Icon slot="start" icon={IconGithub} />
|
||||
Connect to GitHub
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</Empty>
|
||||
</Card.Base>
|
||||
</Layout.Stack>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import Link from '$lib/elements/link.svelte';
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { Tooltip } from '@appwrite.io/pink-svelte';
|
||||
import { timeFromNow, toLocaleDateTime } from '$lib/helpers/date';
|
||||
import DualTimeView from '$lib/components/dualTimeView.svelte';
|
||||
|
||||
export let deployment: Models.Deployment;
|
||||
</script>
|
||||
|
||||
<p>
|
||||
{#if deployment.providerCommitAuthor}
|
||||
<Tooltip>
|
||||
<span>
|
||||
{capitalize(timeFromNow(deployment.$updatedAt))}
|
||||
</span>
|
||||
<span slot="tooltip">{toLocaleDateTime(deployment.$updatedAt)}</span>
|
||||
</Tooltip>
|
||||
by <Link href={deployment.providerCommitAuthorUrl} external
|
||||
>{deployment.providerCommitAuthor}</Link>
|
||||
{:else}
|
||||
<DualTimeView time={deployment.$updatedAt} />
|
||||
{/if}
|
||||
</p>
|
||||
@@ -0,0 +1,52 @@
|
||||
<script lang="ts">
|
||||
import { Trim } from '$lib/components';
|
||||
import { Link } from '$lib/elements';
|
||||
import { protocol } from '$routes/(console)/store';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { IconExternalLink } from '@appwrite.io/pink-icons-svelte';
|
||||
import { ActionMenu, Icon, Layout, Popover, Tag, Typography } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let domains: Models.ProxyRuleList;
|
||||
</script>
|
||||
|
||||
<Layout.Stack gap="xxs" direction="row" alignItems="center">
|
||||
{#if domains?.total}
|
||||
<Link external href={`${$protocol}${domains.rules[0]?.domain}`} variant="muted">
|
||||
<Layout.Stack gap="xxs" direction="row" alignItems="center">
|
||||
<Trim alternativeTrim>
|
||||
<Typography.Text variant="m-400" color="--fgcolor-neutral-primary">
|
||||
{domains.rules[0]?.domain}
|
||||
</Typography.Text>
|
||||
</Trim>
|
||||
<Icon icon={IconExternalLink} size="s" />
|
||||
</Layout.Stack>
|
||||
</Link>
|
||||
{#if domains.rules.length > 1}
|
||||
<Popover padding="none" let:toggle>
|
||||
<Tag size="s" on:click={toggle}>
|
||||
+{domains.rules.length - 1}
|
||||
</Tag>
|
||||
<svelte:fragment slot="tooltip">
|
||||
<ActionMenu.Root>
|
||||
{#each domains.rules as rule, i}
|
||||
{#if i !== 0}
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`${$protocol}${rule.domain}`}
|
||||
external
|
||||
leadingIcon={IconExternalLink}>
|
||||
<Trim alternativeTrim>
|
||||
{rule.domain}
|
||||
</Trim>
|
||||
</ActionMenu.Item.Anchor>
|
||||
{/if}
|
||||
{/each}
|
||||
</ActionMenu.Root>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
{:else}
|
||||
<Typography.Text variant="m-400" color="--fgcolor-neutral-primary">
|
||||
No domains available
|
||||
</Typography.Text>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
@@ -0,0 +1,68 @@
|
||||
<script lang="ts">
|
||||
import { Trim } from '$lib/components';
|
||||
import { Link } from '$lib/elements';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import {
|
||||
IconCode,
|
||||
IconGitBranch,
|
||||
IconGitCommit,
|
||||
IconGithub,
|
||||
IconTerminal
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import { ActionMenu, Layout, Popover, Icon } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let deployment: Models.Deployment;
|
||||
</script>
|
||||
|
||||
{#if deployment.type === 'vcs'}
|
||||
<Popover padding="none" let:toggle>
|
||||
<div>
|
||||
<Link
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
toggle(e);
|
||||
}}>
|
||||
<Layout.Stack direction="row" gap="xs" alignItems="center">
|
||||
<Icon icon={IconGithub} size="s" /> GitHub
|
||||
</Layout.Stack>
|
||||
</Link>
|
||||
</div>
|
||||
<svelte:fragment slot="tooltip">
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Anchor
|
||||
href={deployment.providerRepositoryUrl}
|
||||
external
|
||||
leadingIcon={IconGithub}>
|
||||
{deployment.providerRepositoryOwner}/{deployment.providerRepositoryName}
|
||||
</ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor
|
||||
href={deployment.providerBranchUrl}
|
||||
external
|
||||
leadingIcon={IconGitBranch}>
|
||||
{deployment.providerBranch}
|
||||
</ActionMenu.Item.Anchor>
|
||||
{#if deployment?.providerCommitMessage && deployment?.providerCommitHash && deployment?.providerCommitUrl}
|
||||
<ActionMenu.Item.Anchor
|
||||
href={deployment.providerCommitUrl}
|
||||
external
|
||||
leadingIcon={IconGitCommit}>
|
||||
<Trim alternativeTrim>
|
||||
{deployment?.providerCommitHash?.substring(0, 7)}
|
||||
{deployment.providerCommitMessage.substring(0, 15)}...
|
||||
</Trim>
|
||||
</ActionMenu.Item.Anchor>
|
||||
{/if}
|
||||
</ActionMenu.Root>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{:else if deployment.type === 'manual'}
|
||||
<Layout.Stack gap="s" direction="row" alignItems="center">
|
||||
<Icon icon={IconCode} size="s" /> <span>Manual</span>
|
||||
</Layout.Stack>
|
||||
{:else if deployment.type === 'cli'}
|
||||
<Layout.Stack gap="s" direction="row" alignItems="center">
|
||||
<Icon icon={IconTerminal} size="s" /> <span>CLI</span>
|
||||
</Layout.Stack>
|
||||
{:else}
|
||||
<span>N/A</span>
|
||||
{/if}
|
||||
@@ -2,3 +2,8 @@ export { default as Repositories } from './repositories.svelte';
|
||||
export { default as ConnectGit } from './connectGit.svelte';
|
||||
export { default as NewRepository } from './newRepository.svelte';
|
||||
export { default as RepositoryBehaviour } from './repositoryBehaviour.svelte';
|
||||
export { default as DeploymentCreatedBy } from './deploymentCreatedBy.svelte';
|
||||
export { default as DeploymentSource } from './deploymentSource.svelte';
|
||||
export { default as DeploymentDomains } from './deploymentDomains.svelte';
|
||||
export { default as ConnectBehaviour } from './connectBehaviour.svelte';
|
||||
export { default as ProductionBranchFieldset } from './productionBranchFieldset.svelte';
|
||||
|
||||
@@ -1,45 +1,92 @@
|
||||
<script lang="ts">
|
||||
import { Button, InputSelect, InputText } from '$lib/elements/forms';
|
||||
import { Fieldset, Layout, Selector } from '@appwrite.io/pink-svelte';
|
||||
import SelectRootModal from '../../../routes/(console)/project-[project]/sites/(components)/selectRootModal.svelte';
|
||||
import { Fieldset, Layout, Selector, Skeleton } from '@appwrite.io/pink-svelte';
|
||||
import SelectRootModal from './selectRootModal.svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { sortBranches } from '$lib/stores/vcs';
|
||||
|
||||
export let branch: string;
|
||||
export let rootDir: string;
|
||||
export let options: { value: string; label: string }[] = [];
|
||||
export let silentMode: boolean;
|
||||
export let installationId: string;
|
||||
export let repositoryId: string;
|
||||
|
||||
let show = false;
|
||||
|
||||
async function loadBranches() {
|
||||
const { branches } = await sdk.forProject.vcs.listRepositoryBranches(
|
||||
installationId,
|
||||
repositoryId
|
||||
);
|
||||
const sorted = sortBranches(branches);
|
||||
branch = sorted[0]?.name ?? null;
|
||||
|
||||
if (!branch) {
|
||||
branch = 'main';
|
||||
}
|
||||
|
||||
return sorted;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Fieldset legend="Branch">
|
||||
<Layout.Stack gap="xl">
|
||||
<InputSelect
|
||||
required
|
||||
id="branch"
|
||||
label="Production branch"
|
||||
placeholder="Select branch"
|
||||
isSearchable
|
||||
bind:value={branch}
|
||||
on:select={(event) => {
|
||||
branch = event.detail.value;
|
||||
}}
|
||||
{options} />
|
||||
<Layout.Stack direction="row" gap="s" alignItems="flex-end">
|
||||
<InputText
|
||||
id="root"
|
||||
label="Root directory"
|
||||
placeholder="Select directory"
|
||||
bind:value={rootDir} />
|
||||
<Button secondary size="s" on:click={() => (show = true)}>Select</Button>
|
||||
{#await loadBranches()}
|
||||
<Layout.Stack gap="xl">
|
||||
<Layout.Stack gap="xs">
|
||||
<Skeleton variant="line" width={100} height={20} />
|
||||
<Skeleton variant="line" width="100%" height={32} />
|
||||
</Layout.Stack>
|
||||
<Layout.Stack gap="xs">
|
||||
<Skeleton variant="line" width={100} height={20} />
|
||||
<Skeleton variant="line" width="100%" height={32} />
|
||||
</Layout.Stack>
|
||||
<Layout.Stack gap="xs">
|
||||
<Skeleton variant="line" width={100} height={20} />
|
||||
<Skeleton variant="line" width={300} height={15} />
|
||||
<Skeleton variant="line" width={300} height={15} />
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
{:then branches}
|
||||
{@const options =
|
||||
branches
|
||||
?.map((branch) => {
|
||||
return {
|
||||
value: branch.name,
|
||||
label: branch.name
|
||||
};
|
||||
})
|
||||
?.sort((a, b) => {
|
||||
return a.label > b.label ? 1 : -1;
|
||||
}) ?? []}
|
||||
<Layout.Stack gap="xl">
|
||||
<InputSelect
|
||||
required
|
||||
id="branch"
|
||||
label="Production branch"
|
||||
placeholder="Select branch"
|
||||
isSearchable
|
||||
bind:value={branch}
|
||||
on:select={(event) => {
|
||||
branch = event.detail.value;
|
||||
}}
|
||||
{options} />
|
||||
<Layout.Stack direction="row" gap="s" alignItems="flex-end">
|
||||
<InputText
|
||||
id="root"
|
||||
label="Root directory"
|
||||
placeholder="Select directory"
|
||||
bind:value={rootDir} />
|
||||
<Button secondary size="s" on:click={() => (show = true)}>Select</Button>
|
||||
</Layout.Stack>
|
||||
|
||||
<Selector.Checkbox
|
||||
size="s"
|
||||
id="silentMode"
|
||||
label="Silent mode"
|
||||
description="If selected, comments will not be created when pushing changes to this repository."
|
||||
bind:checked={silentMode} />
|
||||
</Layout.Stack>
|
||||
<Selector.Checkbox
|
||||
size="s"
|
||||
id="silentMode"
|
||||
label="Silent mode"
|
||||
description="If selected, comments will not be created when pushing changes to this repository."
|
||||
bind:checked={silentMode} />
|
||||
</Layout.Stack>
|
||||
{/await}
|
||||
</Fieldset>
|
||||
|
||||
{#if show}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import { EmptySearch } from '$lib/components';
|
||||
import { EmptySearch, Paginator } from '$lib/components';
|
||||
import { Button, InputSearch, InputSelect } from '$lib/elements/forms';
|
||||
import { timeFromNow } from '$lib/helpers/date';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { repositories } from '$routes/(console)/project-[project]/functions/function-[function]/store';
|
||||
import { installation, installations, repository } from '$lib/stores/vcs';
|
||||
@@ -21,6 +19,7 @@
|
||||
import ConnectGit from './connectGit.svelte';
|
||||
import SvgIcon from '../svgIcon.svelte';
|
||||
import { getFrameworkIcon } from '$routes/(console)/project-[project]/sites/store';
|
||||
import { VCSDetectionType, type Models } from '@appwrite.io/console';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
@@ -31,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) {
|
||||
@@ -57,16 +58,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
let search = '';
|
||||
async function loadRepositories(installationId: string, search: string) {
|
||||
if (
|
||||
!$repositories ||
|
||||
$repositories.installationId !== installationId ||
|
||||
$repositories.search !== search
|
||||
) {
|
||||
$repositories.repositories = (
|
||||
await sdk.forProject.vcs.listRepositories(installationId, search || undefined)
|
||||
).providerRepositories;
|
||||
//TODO: remove forced cast after backend fixes
|
||||
if (product === 'functions') {
|
||||
$repositories.repositories = (
|
||||
(await sdk.forProject.vcs.listRepositories(
|
||||
installationId,
|
||||
VCSDetectionType.Runtime,
|
||||
search || undefined
|
||||
)) as unknown as Models.ProviderRepositoryRuntimeList
|
||||
).runtimeProviderRepositories;
|
||||
} else {
|
||||
$repositories.repositories = (
|
||||
await sdk.forProject.vcs.listRepositories(
|
||||
installationId,
|
||||
VCSDetectionType.Framework,
|
||||
search || undefined
|
||||
)
|
||||
).frameworkProviderRepositories;
|
||||
}
|
||||
}
|
||||
|
||||
$repositories.search = search;
|
||||
@@ -77,13 +92,7 @@
|
||||
$repository = $repositories.repositories[0];
|
||||
}
|
||||
|
||||
return $repositories.repositories.slice(0, 4);
|
||||
}
|
||||
|
||||
async function detectFramework(repo) {
|
||||
console.log(repo);
|
||||
// TODO add code once backend is implemented
|
||||
return '';
|
||||
return $repositories.repositories;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -145,79 +154,87 @@
|
||||
</Table.Root>
|
||||
{:then response}
|
||||
{#if response?.length}
|
||||
<Table.Root>
|
||||
{#each response as repo}
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Layout.Stack direction="row" alignItems="center" gap="s">
|
||||
{#if action === 'select'}
|
||||
<input
|
||||
class="is-small u-margin-inline-end-8"
|
||||
type="radio"
|
||||
name="repositories"
|
||||
bind:group={selectedRepository}
|
||||
on:change={() => repository.set(repo)}
|
||||
value={repo.id} />
|
||||
{/if}
|
||||
{#if product === 'sites'}
|
||||
{#await detectFramework(repo)}
|
||||
<Avatar size="xs" alt={repo.name} empty />
|
||||
{:then framework}
|
||||
<Avatar
|
||||
size="xs"
|
||||
alt={repo.name}
|
||||
empty={!framework}>
|
||||
<SvgIcon name={getFrameworkIcon(framework)} />
|
||||
</Avatar>
|
||||
{/await}
|
||||
{:else}
|
||||
<Avatar
|
||||
size="xs"
|
||||
src={repo?.runtime
|
||||
? `${base}/icons/${$app.themeInUse}/color/${
|
||||
repo.runtime.split('-')[0]
|
||||
}.svg`
|
||||
: ''}
|
||||
alt={repo.name} />
|
||||
{/if}
|
||||
<Layout.Stack gap="s" direction="row" alignItems="center">
|
||||
<Typography.Text
|
||||
truncate
|
||||
color="--fgcolor-neutral-secondary">
|
||||
{repo.name}
|
||||
</Typography.Text>
|
||||
{#if repo.private}
|
||||
<Icon
|
||||
size="s"
|
||||
icon={IconLockClosed}
|
||||
color="--fgcolor-neutral-tertiary" />
|
||||
<Paginator
|
||||
items={response}
|
||||
let:paginatedItems
|
||||
hideFooter={response?.length <= 6}
|
||||
limit={6}>
|
||||
<Table.Root>
|
||||
{#each paginatedItems as repo}
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Layout.Stack direction="row" alignItems="center" gap="s">
|
||||
{#if action === 'select'}
|
||||
<input
|
||||
class="is-small u-margin-inline-end-8"
|
||||
type="radio"
|
||||
name="repositories"
|
||||
bind:group={selectedRepository}
|
||||
on:change={() => repository.set(repo)}
|
||||
value={repo.id} />
|
||||
{/if}
|
||||
<time datetime={repo.pushedAt}>
|
||||
<Typography.Caption
|
||||
variant="400"
|
||||
{#if product === 'sites'}
|
||||
{#if repo?.framework && repo.framework !== 'other'}
|
||||
<Avatar size="xs" alt={repo.name}>
|
||||
<SvgIcon
|
||||
name={getFrameworkIcon(repo.framework)}
|
||||
iconSize="small" />
|
||||
</Avatar>
|
||||
{:else}
|
||||
<Avatar size="xs" alt={repo.name} empty />
|
||||
{/if}
|
||||
{:else}
|
||||
{@const iconName = repo?.runtime
|
||||
? repo.runtime.split('-')[0]
|
||||
: undefined}
|
||||
<Avatar size="xs" alt={repo.name} empty={!iconName}>
|
||||
<SvgIcon name={iconName} iconSize="small" />
|
||||
</Avatar>
|
||||
{/if}
|
||||
<Layout.Stack
|
||||
gap="s"
|
||||
direction="row"
|
||||
alignItems="center">
|
||||
<Typography.Text
|
||||
truncate
|
||||
color="--fgcolor-neutral-tertiary">
|
||||
{timeFromNow(repo.pushedAt)}
|
||||
</Typography.Caption>
|
||||
</time>
|
||||
</Layout.Stack>
|
||||
{#if action === 'button'}
|
||||
<Layout.Stack direction="row" justifyContent="flex-end">
|
||||
<PinkButton.Button
|
||||
size="xs"
|
||||
variant="secondary"
|
||||
on:click={() => dispatch('connect', repo)}>
|
||||
Connect
|
||||
</PinkButton.Button>
|
||||
color="--fgcolor-neutral-secondary">
|
||||
{repo.name}
|
||||
</Typography.Text>
|
||||
{#if repo.private}
|
||||
<Icon
|
||||
size="s"
|
||||
icon={IconLockClosed}
|
||||
color="--fgcolor-neutral-tertiary" />
|
||||
{/if}
|
||||
<time datetime={repo.pushedAt}>
|
||||
<Typography.Caption
|
||||
variant="400"
|
||||
truncate
|
||||
color="--fgcolor-neutral-tertiary">
|
||||
{timeFromNow(repo.pushedAt)}
|
||||
</Typography.Caption>
|
||||
</time>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
{#if action === 'button'}
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
justifyContent="flex-end">
|
||||
<PinkButton.Button
|
||||
size="xs"
|
||||
variant="secondary"
|
||||
on:click={() => dispatch('connect', repo)}>
|
||||
Connect
|
||||
</PinkButton.Button>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
</Paginator>
|
||||
{:else}
|
||||
<EmptySearch hidePages bind:search target="repositories">
|
||||
<EmptySearch hidePages hidePagination bind:search target="repositories">
|
||||
<svelte:fragment slot="actions">
|
||||
{#if search}
|
||||
<Button secondary on:click={() => (search = '')}>
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
<script lang="ts">
|
||||
import { Modal } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { iconPath } from '$lib/stores/app';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { installation, repository } from '$lib/stores/vcs';
|
||||
import { VCSDetectionType } from '@appwrite.io/console';
|
||||
import { DirectoryPicker } from '@appwrite.io/pink-svelte';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
type Directory = {
|
||||
title: string;
|
||||
fullPath: string;
|
||||
fileCount: number;
|
||||
thumbnailUrl: string;
|
||||
children?: Directory[];
|
||||
};
|
||||
|
||||
export let show = false;
|
||||
export let rootDir: string;
|
||||
export let product: 'sites' | 'functions';
|
||||
|
||||
let isLoading = true;
|
||||
let directories: Directory[] = [
|
||||
{
|
||||
title: 'Root',
|
||||
fullPath: './',
|
||||
fileCount: 0,
|
||||
thumbnailUrl: 'root',
|
||||
children: []
|
||||
}
|
||||
];
|
||||
let currentPath: string = './';
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
const content = await sdk.forProject.vcs.getRepositoryContents(
|
||||
$installation.$id,
|
||||
$repository.id,
|
||||
currentPath
|
||||
);
|
||||
// console.log(content);
|
||||
directories[0].fileCount = content.contents?.length ?? 0;
|
||||
directories[0].children = content.contents
|
||||
.filter((e) => e.isDirectory)
|
||||
.map((dir) => ({
|
||||
title: dir.name,
|
||||
fullPath: currentPath + dir.name,
|
||||
fileCount: undefined,
|
||||
thumbnailUrl: dir.name
|
||||
}));
|
||||
// console.log(directories);
|
||||
isLoading = false;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
|
||||
async function fetchContents(e: CustomEvent) {
|
||||
const path = e.detail.fullPath;
|
||||
currentPath = path;
|
||||
try {
|
||||
const content = await sdk.forProject.vcs.getRepositoryContents(
|
||||
$installation.$id,
|
||||
$repository.id,
|
||||
path
|
||||
);
|
||||
// console.log(content);
|
||||
const fileCount = content.contents?.length ?? 0;
|
||||
const contentDirectories = content.contents.filter((e) => e.isDirectory);
|
||||
|
||||
if (contentDirectories.length === 0) {
|
||||
return;
|
||||
}
|
||||
let currentDir = directories[0];
|
||||
for (let i = 1; i < path.split('/').length; i++) {
|
||||
const dir = path.split('/')[i];
|
||||
currentDir = currentDir.children.find((d) => d.title === dir);
|
||||
}
|
||||
|
||||
currentDir.fileCount = fileCount;
|
||||
currentDir.children = contentDirectories.map((dir) => ({
|
||||
title: dir.name,
|
||||
fullPath: path + '/' + dir.name,
|
||||
fileCount: undefined,
|
||||
thumbnailUrl: undefined
|
||||
}));
|
||||
const runtime = await sdk.forProject.vcs.createRepositoryDetection(
|
||||
$installation.$id,
|
||||
$repository.id,
|
||||
VCSDetectionType.Framework, //TODO: add type: VCSDetectionType.Framework || VCSDetectionType.Runtime according to the product
|
||||
path
|
||||
);
|
||||
//TODO: Fix runtime after passing type: runtime.framework || runtime.runtime
|
||||
currentDir.children.forEach((dir) => {
|
||||
dir.thumbnailUrl = $iconPath(runtime.framework, 'color');
|
||||
});
|
||||
directories = [...directories];
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
rootDir = currentPath;
|
||||
show = false;
|
||||
}
|
||||
|
||||
// $: console.log(rootDir);
|
||||
</script>
|
||||
|
||||
<Modal title="Root directory" bind:show onSubmit={handleSubmit}>
|
||||
<span slot="description">
|
||||
Select the directory where your site code is located using the menu below.
|
||||
</span>
|
||||
<DirectoryPicker {directories} {isLoading} on:select={fetchContents} />
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button submit>Save</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
Reference in New Issue
Block a user