mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
feat: template flows (WIP)
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
<script lang="ts">
|
||||
import { app } from '$lib/stores/app';
|
||||
|
||||
import { Typography } from '@appwrite.io/pink-svelte';
|
||||
</script>
|
||||
|
||||
<div class="auth-bg">
|
||||
<section class="console-container">
|
||||
<slot />
|
||||
</section>
|
||||
<footer>
|
||||
<Typography.Eyebrow color="--fgcolor-neutral-secondary">POWERED BY</Typography.Eyebrow>
|
||||
{#if $app.themeInUse === 'dark'}
|
||||
<img
|
||||
src="/console/images/appwrite-logo-dark.svg"
|
||||
width="120"
|
||||
height="22"
|
||||
alt="Appwrite Logo" />
|
||||
{:else}
|
||||
<img
|
||||
src="/console/images/appwrite-logo-light.svg"
|
||||
width="120"
|
||||
height="22"
|
||||
alt="Appwrite Logo" />
|
||||
{/if}
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.auth-bg {
|
||||
position: fixed;
|
||||
background: var(--bgcolor-neutral-primary, #fff);
|
||||
background-size: cover;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-content: space-between;
|
||||
section {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
footer {
|
||||
padding: 2rem 1rem;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,32 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { SvgIcon } from '$lib/components/index.js';
|
||||
import { Button, InputSelect } from '$lib/elements/forms';
|
||||
import { Button, Form, InputSelect } from '$lib/elements/forms';
|
||||
import { app } from '$lib/stores/app';
|
||||
import type { Organization } from '$lib/stores/organization';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { getFrameworkIcon } from '$lib/stores/sites.js';
|
||||
import { OAuthProvider, Query, type Models } from '@appwrite.io/console';
|
||||
import { IconGithub } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Card, Divider, Icon, Image, Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let data;
|
||||
|
||||
let projects: Models.ProjectList;
|
||||
let selectedOrg: Organization = data?.organizations?.total
|
||||
? data.organizations.teams[0]
|
||||
: undefined;
|
||||
onMount(async () => {
|
||||
if (data.organizations) {
|
||||
projects = await sdk.forConsole.projects.list([
|
||||
Query.equal('teamId', selectedOrg.$id),
|
||||
Query.orderDesc('')
|
||||
]);
|
||||
}
|
||||
});
|
||||
let selectedProject: string;
|
||||
let selectedOrg = data?.organizations?.total ? data.organizations.teams[0].$id : undefined;
|
||||
|
||||
export function isSiteTemplate(
|
||||
function isSiteTemplate(
|
||||
template: Models.TemplateFunction | Models.TemplateSite,
|
||||
product: 'site' | 'function'
|
||||
): template is Models.TemplateSite {
|
||||
@@ -41,142 +32,140 @@
|
||||
['read:user', 'user:email']
|
||||
);
|
||||
}
|
||||
|
||||
async function fetchProjects() {
|
||||
projects = await sdk.forConsole.projects.list([
|
||||
Query.equal('teamId', selectedOrg),
|
||||
Query.orderDesc('')
|
||||
]);
|
||||
selectedProject = projects.projects[0].$id;
|
||||
}
|
||||
|
||||
function generateUrl() {
|
||||
if (isSiteTemplate(data.template, data.product)) {
|
||||
return `${base}/project-${selectedProject}/sites/create-site/templates/template-${data.template.key}`;
|
||||
} else {
|
||||
return `${base}/project-${selectedProject}/functions/create-function/templates/template-${data.template.name}`;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (selectedProject !== null) {
|
||||
await goto(
|
||||
`${base}/template-${$page.params.template}/redirect-${selectedOrg}?route=${generateUrl()}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$: if (selectedOrg !== undefined) {
|
||||
fetchProjects();
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Deploy {data.template.name} - Appwrite</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="auth-bg">
|
||||
<section class="console-container">
|
||||
<Card.Base padding="s" radius="l">
|
||||
<Layout.Stack gap="xl">
|
||||
<Layout.Stack gap="l">
|
||||
<Typography.Title size="l">
|
||||
Deploy {data.product}
|
||||
</Typography.Title>
|
||||
<Card.Base variant="secondary" padding="xxxs" radius="s">
|
||||
<Layout.GridFraction start={4} end={5}>
|
||||
{#if isSiteTemplate(data.template, data.product)}
|
||||
{@const framework = data.template.frameworks[0]}
|
||||
<Image
|
||||
border
|
||||
radius="s"
|
||||
ratio="16/9"
|
||||
style=" align-self: start"
|
||||
src={$app.themeInUse === 'dark'
|
||||
? data.template.screenshotDark ||
|
||||
`${base}/images/sites/screenshot-placeholder-dark.svg`
|
||||
: data.template.screenshotLight ||
|
||||
`${base}/images/sites/screenshot-placeholder-light.svg`}
|
||||
alt="Screenshot" />
|
||||
<Layout.Stack gap="xl">
|
||||
<Layout.Stack gap="xxs">
|
||||
<Typography.Text
|
||||
variant="m-500"
|
||||
color="--fgcolor-neutral-primary">
|
||||
{data.template.name}
|
||||
</Typography.Text>
|
||||
<Typography.Text variant="m-500">
|
||||
{data.template.tagline}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
{@const frameworkIcon = getFrameworkIcon(framework.key)}
|
||||
<Layout.Stack gap="xxs" alignItems="center" direction="row">
|
||||
{#if frameworkIcon}
|
||||
<SvgIcon
|
||||
iconSize="small"
|
||||
size={16}
|
||||
name={frameworkIcon} />
|
||||
{/if}
|
||||
<Typography.Text
|
||||
variant="m-500"
|
||||
color="--fgcolor-neutral-primary">
|
||||
{framework.name}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
<div style:max-width="592px" style:width="100%">
|
||||
<Card.Base padding="s" radius="l">
|
||||
<Layout.Stack gap="xl">
|
||||
<Layout.Stack gap="l">
|
||||
<Typography.Title size="l">
|
||||
Deploy {data.product}
|
||||
</Typography.Title>
|
||||
<Card.Base variant="secondary" padding="xxxs" radius="s">
|
||||
<Layout.GridFraction start={4} end={5}>
|
||||
{#if isSiteTemplate(data.template, data.product)}
|
||||
{@const framework = data.template.frameworks[0]}
|
||||
<Image
|
||||
border
|
||||
radius="s"
|
||||
ratio="16/9"
|
||||
style=" align-self: start"
|
||||
src={$app.themeInUse === 'dark'
|
||||
? data.template.screenshotDark ||
|
||||
`${base}/images/sites/screenshot-placeholder-dark.svg`
|
||||
: data.template.screenshotLight ||
|
||||
`${base}/images/sites/screenshot-placeholder-light.svg`}
|
||||
alt="Screenshot" />
|
||||
<Layout.Stack gap="xl">
|
||||
<Layout.Stack gap="xxs">
|
||||
<Typography.Text
|
||||
variant="m-500"
|
||||
color="--fgcolor-neutral-primary">
|
||||
{data.template.name}
|
||||
</Typography.Text>
|
||||
<Typography.Text variant="m-500">
|
||||
{data.template.tagline}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</Layout.GridFraction>
|
||||
</Card.Base>
|
||||
{@const frameworkIcon = getFrameworkIcon(framework.key)}
|
||||
<Layout.Stack gap="xxs" alignItems="center" direction="row">
|
||||
{#if frameworkIcon}
|
||||
<SvgIcon iconSize="small" size={16} name={frameworkIcon} />
|
||||
{/if}
|
||||
<Typography.Text
|
||||
variant="m-500"
|
||||
color="--fgcolor-neutral-primary">
|
||||
{framework.name}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</Layout.GridFraction>
|
||||
</Card.Base>
|
||||
|
||||
{#if data.account}
|
||||
<InputSelect
|
||||
id="organization"
|
||||
label="Organization"
|
||||
placeholder="Select an organization"
|
||||
options={data.organizations.teams.map((o) => ({
|
||||
label: o.name,
|
||||
value: o.$id
|
||||
}))}
|
||||
bind:value={selectedOrg.$id} />
|
||||
{#if data.account}
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Layout.Stack gap="xl">
|
||||
<InputSelect
|
||||
id="organization"
|
||||
label="Organization"
|
||||
required
|
||||
placeholder="Select an organization"
|
||||
options={data.organizations.teams.map((o) => ({
|
||||
label: o.name,
|
||||
value: o.$id
|
||||
}))}
|
||||
bind:value={selectedOrg} />
|
||||
|
||||
<Divider />
|
||||
<Button secondary fullWidth on:click={onGithubLogin}>
|
||||
<Icon slot="start" icon={IconGithub} />
|
||||
<span class="text">Sign in with GitHub</span>
|
||||
</Button>
|
||||
{:else}
|
||||
<Typography.Text variant="m-500">
|
||||
Sign in to deploy a site from GitHub.
|
||||
</Typography.Text>
|
||||
{#key selectedOrg}
|
||||
{#if projects?.total}
|
||||
<InputSelect
|
||||
id="project"
|
||||
label="Project"
|
||||
required
|
||||
options={projects.projects.map((p) => ({
|
||||
label: p.name,
|
||||
value: p.$id
|
||||
}))}
|
||||
bind:value={selectedProject} />
|
||||
{/if}
|
||||
{/key}
|
||||
<Divider />
|
||||
<Layout.Stack direction="row-reverse">
|
||||
{#if selectedProject !== null}
|
||||
<div>
|
||||
<Button disabled={!selectedOrg || !selectedProject} submit>
|
||||
<span class="text">Continue</span>
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Form>
|
||||
{:else}
|
||||
<Typography.Text variant="m-500">
|
||||
Sign in to deploy a site from GitHub.
|
||||
</Typography.Text>
|
||||
|
||||
<Divider />
|
||||
<Button secondary fullWidth on:click={onGithubLogin}>
|
||||
<Icon slot="start" icon={IconGithub} />
|
||||
<span class="text">Sign in with GitHub</span>
|
||||
</Button>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
<Divider />
|
||||
<Button secondary fullWidth on:click={onGithubLogin}>
|
||||
<Icon slot="start" icon={IconGithub} />
|
||||
<span class="text">Sign in with GitHub</span>
|
||||
</Button>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Card.Base>
|
||||
</section>
|
||||
<footer>
|
||||
<Typography.Eyebrow color="--fgcolor-neutral-secondary">POWERED BY</Typography.Eyebrow>
|
||||
{#if $app.themeInUse === 'dark'}
|
||||
<img
|
||||
src="/console/images/appwrite-logo-dark.svg"
|
||||
width="120"
|
||||
height="22"
|
||||
alt="Appwrite Logo" />
|
||||
{:else}
|
||||
<img
|
||||
src="/console/images/appwrite-logo-light.svg"
|
||||
width="120"
|
||||
height="22"
|
||||
alt="Appwrite Logo" />
|
||||
{/if}
|
||||
</footer>
|
||||
</Layout.Stack>
|
||||
</Card.Base>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.auth-bg {
|
||||
position: fixed;
|
||||
background: var(--bgcolor-neutral-primary, #fff);
|
||||
background-size: cover;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-content: space-between;
|
||||
section {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 592px;
|
||||
}
|
||||
footer {
|
||||
padding: 2rem 1rem;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { sdk } from '$lib/stores/sdk.js';
|
||||
import { isCloud } from '$lib/system.js';
|
||||
// import { isCloud } from '$lib/system.js';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
// import { redirect } from '@sveltejs/kit';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
export const load = async ({ parent, url, params }) => {
|
||||
@@ -28,13 +28,11 @@ export const load = async ({ parent, url, params }) => {
|
||||
error(404, 'Type is not valid');
|
||||
}
|
||||
|
||||
if (account)
|
||||
return {
|
||||
account,
|
||||
template: template,
|
||||
product,
|
||||
organizations: account?.$id
|
||||
? await sdk.forConsole.billing.listOrganization()
|
||||
: undefined
|
||||
};
|
||||
return {
|
||||
account,
|
||||
template: template,
|
||||
product,
|
||||
// organizations: account?.$id ? await sdk.forConsole.billing.listOrganization() : undefined
|
||||
organizations: account?.$id ? await sdk.forConsole.teams.list() : undefined
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
import { goto } from '$app/navigation';
|
||||
import { Layout, Spinner, Typography } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let data;
|
||||
onMount(() => {
|
||||
goto(data.route);
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Redirecting - Appwrite</title>
|
||||
</svelte:head>
|
||||
|
||||
<Layout.Stack alignItems="center" justifyContent="center" style="max-width: 400px">
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="center" gap="s">
|
||||
<Spinner size="s" />
|
||||
<Typography.Text>Redirecting...</Typography.Text>
|
||||
</Layout.Stack>
|
||||
<Typography.Title size="xl" align="center">Please wait while we redirect you</Typography.Title>
|
||||
</Layout.Stack>
|
||||
@@ -0,0 +1,14 @@
|
||||
import { sdk } from '$lib/stores/sdk.js';
|
||||
|
||||
export const load = async ({ params, url }) => {
|
||||
// const { account } = await parent();
|
||||
const prefs = await sdk.forConsole.account.getPrefs();
|
||||
if (prefs.organization !== params.organization) {
|
||||
const newPrefs = { ...prefs, organization: params.organization };
|
||||
sdk.forConsole.account.updatePrefs(newPrefs);
|
||||
}
|
||||
return {
|
||||
organization: await sdk.forConsole.teams.get(params.organization),
|
||||
route: url.searchParams.get('route')
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user