feat: template flows (WIP)

This commit is contained in:
Arman
2025-03-27 18:36:34 +01:00
parent 6fe23a8515
commit ab7adf4d4e
5 changed files with 236 additions and 153 deletions
@@ -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')
};
};