mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'mgn-dv' of https://github.com/appwrite/console into mgn-dv
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
<script>
|
||||
import { Divider, Card, Typography, Layout, Button, Icon } from '@appwrite.io/pink-svelte';
|
||||
import { IconArrowUp, IconChevronLeft, IconPaperClip } from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
export let showChat;
|
||||
</script>
|
||||
|
||||
<section class="chat" class:is-visible={showChat}>
|
||||
<Card.Base>
|
||||
<div class="chat-content">
|
||||
<Layout.Stack height="calc(100vh - 120px)" justifyContent="space-between">
|
||||
<Layout.Stack gap="s">
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
alignItems="center">
|
||||
<Typography.Text>Chat</Typography.Text>
|
||||
<Button.Button
|
||||
icon
|
||||
variant="secondary"
|
||||
size="s"
|
||||
on:click={() => {
|
||||
showChat = false;
|
||||
}}
|
||||
><Icon
|
||||
icon={IconChevronLeft}
|
||||
color="--fgcolor-neutral-tertiary" /></Button.Button>
|
||||
</Layout.Stack>
|
||||
|
||||
<Divider />
|
||||
<!-- CHAT CONTENT -->
|
||||
</Layout.Stack>
|
||||
<div class="input">
|
||||
<textarea placeholder="Reply..."></textarea>
|
||||
<div class="options">
|
||||
<Layout.Stack direction="row" justifyContent="flex-end">
|
||||
<Button.Button icon variant="secondary" size="s"
|
||||
><Icon
|
||||
icon={IconPaperClip}
|
||||
color="--fgcolor-neutral-tertiary" /></Button.Button>
|
||||
<Button.Button icon variant="secondary" size="s"
|
||||
><Icon
|
||||
icon={IconArrowUp}
|
||||
color="--fgcolor-neutral-tertiary" /></Button.Button>
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
</Card.Base>
|
||||
</section>
|
||||
<div class="chat-placeholder" class:is-visible={showChat}></div>
|
||||
|
||||
<style lang="scss">
|
||||
.chat {
|
||||
width: 0;
|
||||
position: fixed;
|
||||
top: 48px;
|
||||
overflow: hidden;
|
||||
transition: width 0.3s ease-in-out;
|
||||
|
||||
&.is-visible {
|
||||
width: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-placeholder {
|
||||
width: 0;
|
||||
transition: width 0.3s ease-in-out;
|
||||
&.is-visible {
|
||||
width: 600px;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-content {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.is-visible .chat-content {
|
||||
transition: opacity 0.3s ease-in-out 0.2s;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.input {
|
||||
margin-inline: calc(-1 * var(--space-9));
|
||||
margin-block-end: calc(-1 * var(--space-9));
|
||||
border-top: 1px solid var(--border-neutral);
|
||||
border-radius: var(--border-radius-m);
|
||||
padding: var(--space-6);
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
overflow: hidden;
|
||||
resize: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -81,3 +81,4 @@ export { default as Sidebar } from './sidebar.svelte';
|
||||
export { default as BottomSheet } from './bottom-sheet/index';
|
||||
export { default as Confirm } from './confirm.svelte';
|
||||
export { default as UsageCard } from './usageCard.svelte';
|
||||
export { default as Chat } from './chat.svelte';
|
||||
|
||||
@@ -5,35 +5,45 @@
|
||||
IconCog,
|
||||
IconDatabase,
|
||||
IconFolder,
|
||||
IconSparkles,
|
||||
IconTemplate,
|
||||
IconUserGroup
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import { base } from '$app/paths';
|
||||
|
||||
export let project;
|
||||
export let showChat;
|
||||
</script>
|
||||
|
||||
<nav style:--icon-fill="var(--fgcolor-neutral-tertiary)">
|
||||
<Layout.Stack gap="xs">
|
||||
<ActionMenu.Item.Anchor href={`${base}/project-${project.$id}`} leadingIcon={IconTemplate}
|
||||
></ActionMenu.Item.Anchor>
|
||||
<Divider />
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`${base}/project-${project.$id}/auth`}
|
||||
leadingIcon={IconUserGroup}></ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`${base}/project-${project.$id}/databases`}
|
||||
leadingIcon={IconDatabase}></ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`${base}/project-${project.$id}/messaging`}
|
||||
leadingIcon={IconAnnotation}></ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`${base}/project-${project.$id}/storage`}
|
||||
leadingIcon={IconFolder}></ActionMenu.Item.Anchor>
|
||||
<Divider />
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`${base}/project-${project.$id}/settings`}
|
||||
leadingIcon={IconCog}></ActionMenu.Item.Anchor>
|
||||
<Layout.Stack direction="column" justifyContent="space-between" height="100%">
|
||||
<Layout.Stack gap="xs">
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`${base}/project-${project.$id}`}
|
||||
leadingIcon={IconTemplate}></ActionMenu.Item.Anchor>
|
||||
<Divider />
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`${base}/project-${project.$id}/auth`}
|
||||
leadingIcon={IconUserGroup}></ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`${base}/project-${project.$id}/databases`}
|
||||
leadingIcon={IconDatabase}></ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`${base}/project-${project.$id}/messaging`}
|
||||
leadingIcon={IconAnnotation}></ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`${base}/project-${project.$id}/storage`}
|
||||
leadingIcon={IconFolder}></ActionMenu.Item.Anchor>
|
||||
<Divider />
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`${base}/project-${project.$id}/settings`}
|
||||
leadingIcon={IconCog}></ActionMenu.Item.Anchor>
|
||||
</Layout.Stack>
|
||||
<ActionMenu.Item.Button
|
||||
leadingIcon={IconSparkles}
|
||||
on:click={() => {
|
||||
showChat = !showChat;
|
||||
}}></ActionMenu.Item.Button>
|
||||
</Layout.Stack>
|
||||
</nav>
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
import { isStudio } from '$lib/system';
|
||||
|
||||
export let size: 'small' | 'medium' | 'large' | 'xl' = null;
|
||||
export let blocksize = '152px';
|
||||
@@ -9,16 +10,21 @@
|
||||
: '';
|
||||
</script>
|
||||
|
||||
<div class="top-cover-console" style:block-size={blocksize}>
|
||||
<div class="cover-container" {style}>
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<slot name="header" />
|
||||
</Layout.Stack>
|
||||
<slot />
|
||||
{#if isStudio}
|
||||
<slot name="header" />
|
||||
<slot />
|
||||
{:else}
|
||||
<div class="top-cover-console" class:no-padding={isStudio} style:block-size={blocksize}>
|
||||
<div class="cover-container" {style}>
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<slot name="header" />
|
||||
</Layout.Stack>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
<style lang="scss">
|
||||
.top-cover-console {
|
||||
container-type: inline-size;
|
||||
padding-block-start: var(--base-32);
|
||||
@@ -27,6 +33,11 @@
|
||||
background: var(--bgcolor-neutral-primary, #1d1d21);
|
||||
margin-left: -190px;
|
||||
padding-left: 190px;
|
||||
|
||||
&.no-padding {
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
.cover-container {
|
||||
position: relative;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { AvatarInitials, Breadcrumbs } from '$lib/components/index.js';
|
||||
import { Avatar, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { AvatarInitials, Breadcrumbs, Chat } from '$lib/components/index.js';
|
||||
import { Avatar, Divider, Layout, Card, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { organization, organizationList } from '$lib/stores/organization';
|
||||
import { user } from '$lib/stores/user';
|
||||
@@ -26,6 +26,8 @@
|
||||
projects: loadedProjects
|
||||
};
|
||||
});
|
||||
|
||||
$: showChat = !showChat ? $page.url.pathname.endsWith('builder') : showChat;
|
||||
</script>
|
||||
|
||||
<main>
|
||||
@@ -36,9 +38,24 @@
|
||||
<AvatarInitials name={$user?.name ?? ''} size="s" />
|
||||
</Layout.Stack>
|
||||
</header>
|
||||
<div class="studio-content" class:project-sidebar={hasProjectSidebar}><slot /></div>
|
||||
<div class="studio-content" class:project-sidebar={hasProjectSidebar}>
|
||||
<Layout.Stack direction="row">
|
||||
{#if hasProjectSidebar}
|
||||
<Chat bind:showChat />
|
||||
{/if}
|
||||
|
||||
<Card.Base>
|
||||
<Layout.Stack>
|
||||
{#if $page.data?.header}
|
||||
<svelte:component this={$page.data.header} />
|
||||
{/if}
|
||||
<slot />
|
||||
</Layout.Stack>
|
||||
</Card.Base>
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
{#if hasProjectSidebar}
|
||||
<SidebarProject project={$page.data.project} />
|
||||
<SidebarProject project={$page.data.project} bind:showChat />
|
||||
{:else}
|
||||
<SidebarOrganization organization={$page.data.organization} />
|
||||
{/if}
|
||||
|
||||
@@ -144,30 +144,28 @@
|
||||
</script>
|
||||
|
||||
{#if isStudio}
|
||||
<Card.Base>
|
||||
<Layout.Stack>
|
||||
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Typography.Text variant="m-500" color="--fgcolor-neutral-secondary"
|
||||
>Projects</Typography.Text>
|
||||
<Button size="s">Create project</Button>
|
||||
</Layout.Stack>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Layout.Grid gap="l" columnsS={3} columns={4} columnsL={4} columnsXL={5}>
|
||||
<!-- TODO: this now maps over projects, but probably will be more like artifacts (and location might be different-->
|
||||
{#each data.projects.projects as project}
|
||||
<a href={`${base}/project-${project.$id}`}
|
||||
><Card.Media
|
||||
src="https://picsum.photos/260/150"
|
||||
alt=""
|
||||
description={project.description}
|
||||
title={project.name}></Card.Media
|
||||
></a>
|
||||
{/each}
|
||||
</Layout.Grid>
|
||||
<Layout.Stack>
|
||||
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Typography.Text variant="m-500" color="--fgcolor-neutral-secondary"
|
||||
>Projects</Typography.Text>
|
||||
<Button size="s">Create project</Button>
|
||||
</Layout.Stack>
|
||||
</Card.Base>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Layout.Grid gap="l" columnsS={3} columns={4} columnsL={4} columnsXL={5}>
|
||||
<!-- TODO: this now maps over projects, but probably will be more like artifacts (and location might be different-->
|
||||
{#each data.projects.projects as project}
|
||||
<a href={`${base}/project-${project.$id}`}
|
||||
><Card.Media
|
||||
src="https://picsum.photos/260/150"
|
||||
alt=""
|
||||
description={project.description}
|
||||
title={project.name}></Card.Media
|
||||
></a>
|
||||
{/each}
|
||||
</Layout.Grid>
|
||||
</Layout.Stack>
|
||||
{:else}
|
||||
<Container>
|
||||
<div class="u-flex u-gap-12 common-section u-main-space-between">
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
isBilling,
|
||||
isOwner
|
||||
} from '$lib/stores/roles';
|
||||
import { GRACE_PERIOD_OVERRIDE, isCloud } from '$lib/system';
|
||||
import { GRACE_PERIOD_OVERRIDE, isCloud, isStudio } from '$lib/system';
|
||||
import { IconGithub, IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Icon, Tooltip, Typography, Layout, Badge } from '@appwrite.io/pink-svelte';
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
].filter((tab) => !tab.disabled);
|
||||
</script>
|
||||
|
||||
{#if $organization?.$id}
|
||||
{#if $organization?.$id && !isStudio}
|
||||
<Cover>
|
||||
<svelte:fragment slot="header">
|
||||
<span class="u-flex u-cross-center u-gap-8 u-min-width-0">
|
||||
|
||||
@@ -2,8 +2,12 @@ import { redirect } from '@sveltejs/kit';
|
||||
import { base } from '$app/paths';
|
||||
import type { PageLoad } from './$types';
|
||||
import { hasOnboardingDismissed } from '$lib/helpers/onboarding';
|
||||
import { isStudio } from '$lib/system';
|
||||
|
||||
export const load: PageLoad = async ({ params }) => {
|
||||
if (isStudio) {
|
||||
redirect(302, `${base}/project-${params.project}/builder`);
|
||||
}
|
||||
if (hasOnboardingDismissed(params.project)) {
|
||||
redirect(302, `${base}/project-${params.project}/overview`);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { Cover } from '$lib/layout';
|
||||
import { canWriteProjects } from '$lib/stores/roles';
|
||||
import { Typography } from '@appwrite.io/pink-svelte';
|
||||
import { isStudio } from '$lib/system';
|
||||
|
||||
const projectId = page.params.project;
|
||||
const path = `${base}/project-${projectId}/auth`;
|
||||
@@ -52,7 +53,8 @@
|
||||
|
||||
<Cover>
|
||||
<svelte:fragment slot="header">
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size="xl">Auth</Typography.Title>
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size={isStudio ? 's' : 'xl'}
|
||||
>Auth</Typography.Title>
|
||||
</svelte:fragment>
|
||||
<Tabs>
|
||||
{#each tabs as tab}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Layout, Card, Typography, Divider } from '@appwrite.io/pink-svelte';
|
||||
</script>
|
||||
|
||||
<Layout.Stack direction="row">Iframe</Layout.Stack>
|
||||
@@ -5,6 +5,7 @@
|
||||
import { isTabSelected } from '$lib/helpers/load';
|
||||
import { Cover } from '$lib/layout';
|
||||
import { Typography } from '@appwrite.io/pink-svelte';
|
||||
import { isStudio } from '$lib/system';
|
||||
|
||||
const projectId = page.params.project;
|
||||
const path = `${base}/project-${projectId}/databases`;
|
||||
@@ -26,7 +27,8 @@
|
||||
|
||||
<Cover>
|
||||
<svelte:fragment slot="header">
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size="xl">Databases</Typography.Title>
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size={isStudio ? 's' : 'xl'}
|
||||
>Databases</Typography.Title>
|
||||
</svelte:fragment>
|
||||
<Tabs>
|
||||
{#each tabs as tab}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { isTabSelected } from '$lib/helpers/load';
|
||||
import { Cover } from '$lib/layout';
|
||||
import { Typography } from '@appwrite.io/pink-svelte';
|
||||
import { isStudio } from '$lib/system';
|
||||
|
||||
$: projectId = page.params.project;
|
||||
$: path = `${base}/project-${projectId}/functions`;
|
||||
@@ -30,7 +31,8 @@
|
||||
|
||||
<Cover>
|
||||
<svelte:fragment slot="header">
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size="xl">Functions</Typography.Title>
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size={isStudio ? 's' : 'xl'}
|
||||
>Functions</Typography.Title>
|
||||
</svelte:fragment>
|
||||
|
||||
<Tabs>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { isTabSelected } from '$lib/helpers/load';
|
||||
import { Cover } from '$lib/layout';
|
||||
import { Typography } from '@appwrite.io/pink-svelte';
|
||||
import { isStudio } from '$lib/system';
|
||||
|
||||
const projectId = page.params.project;
|
||||
const path = `${base}/project-${projectId}/messaging`;
|
||||
@@ -31,7 +32,8 @@
|
||||
|
||||
<Cover>
|
||||
<svelte:fragment slot="header">
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size="xl">Messaging</Typography.Title>
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size={isStudio ? 's' : 'xl'}
|
||||
>Messaging</Typography.Title>
|
||||
</svelte:fragment>
|
||||
<Tabs>
|
||||
{#each tabs as tab}
|
||||
|
||||
@@ -11,12 +11,13 @@
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { isStudio } from '$lib/system';
|
||||
</script>
|
||||
|
||||
{#if !page.url.pathname.includes('get-started')}
|
||||
<Cover>
|
||||
<svelte:fragment slot="header">
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size="xl">
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size={isStudio ? 's' : 'xl'}>
|
||||
{$project?.name}
|
||||
</Typography.Title>
|
||||
<Id value={$project.$id}>{$project.$id}</Id>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import type { TabElement } from '$lib/helpers/load';
|
||||
import { Cover } from '$lib/layout';
|
||||
import { canWriteProjects } from '$lib/stores/roles';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { isCloud, isStudio } from '$lib/system';
|
||||
import { Typography } from '@appwrite.io/pink-svelte';
|
||||
|
||||
const projectId = page.params.project;
|
||||
@@ -51,7 +51,8 @@
|
||||
|
||||
<Cover>
|
||||
<svelte:fragment slot="header">
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size="xl">Settings</Typography.Title>
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size={isStudio ? 's' : 'xl'}
|
||||
>Settings</Typography.Title>
|
||||
</svelte:fragment>
|
||||
|
||||
<Tabs>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { isTabSelected } from '$lib/helpers/load';
|
||||
import { Cover } from '$lib/layout';
|
||||
import { Typography } from '@appwrite.io/pink-svelte';
|
||||
import { isStudio } from '$lib/system';
|
||||
|
||||
$: projectId = page.params.project;
|
||||
$: path = `${base}/project-${projectId}/sites`;
|
||||
@@ -25,7 +26,8 @@
|
||||
|
||||
<Cover>
|
||||
<svelte:fragment slot="header">
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size="xl">Sites</Typography.Title>
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size={isStudio ? 's' : 'xl'}
|
||||
>Sites</Typography.Title>
|
||||
</svelte:fragment>
|
||||
|
||||
<Tabs variant="primary" let:root>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { isTabSelected } from '$lib/helpers/load';
|
||||
import { Cover } from '$lib/layout';
|
||||
import { Typography } from '@appwrite.io/pink-svelte';
|
||||
import { isStudio } from '$lib/system';
|
||||
|
||||
const projectId = page.params.project;
|
||||
const path = `${base}/project-${projectId}/storage`;
|
||||
@@ -26,7 +27,8 @@
|
||||
|
||||
<Cover>
|
||||
<svelte:fragment slot="header">
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size="xl">Storage</Typography.Title>
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size={isStudio ? 's' : 'xl'}
|
||||
>Storage</Typography.Title>
|
||||
</svelte:fragment>
|
||||
<Tabs>
|
||||
{#each tabs as tab}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Avatar, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { AvatarInitials, Breadcrumbs } from '$lib/components';
|
||||
import { page } from '$app/stores';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { tierToPlan } from '$lib/stores/billing';
|
||||
import { organization, organizationList } from '$lib/stores/organization';
|
||||
import { user } from '$lib/stores/user';
|
||||
import SidebarOrganization from '$lib/components/studio/sidebarOrganization.svelte';
|
||||
import SidebarProject from '$lib/components/studio/sidebarProject.svelte';
|
||||
|
||||
$: hasProjectSidebar = $page.url.pathname.startsWith('/studio/proj');
|
||||
|
||||
$: loadedProjects = $page.data.projects.map((project) => {
|
||||
return {
|
||||
name: project?.name,
|
||||
$id: project.$id,
|
||||
isSelected: $page.data.currentProjectId === project.$id,
|
||||
platformCount: project.platforms.length,
|
||||
pingCount: project.pingCount
|
||||
};
|
||||
});
|
||||
|
||||
$: organizations = $organizationList.teams.map((org) => {
|
||||
const billingPlan = org['billingPlan'];
|
||||
return {
|
||||
name: org.name,
|
||||
$id: org.$id,
|
||||
showUpgrade: billingPlan === BillingPlan.FREE,
|
||||
tierName: isCloud ? (tierToPlan(billingPlan)?.name ?? '') : null,
|
||||
isSelected: $page.data.currentOrganization?.$id === org.$id,
|
||||
projects: loadedProjects
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<Layout.Stack>
|
||||
<header>
|
||||
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Breadcrumbs {organizations} />
|
||||
<AvatarInitials name={$user?.name ?? ''} size="s" />
|
||||
</Layout.Stack>
|
||||
</header>
|
||||
<div class="studio-content" class:project-sidebar={hasProjectSidebar}><slot /></div>
|
||||
{#if hasProjectSidebar}
|
||||
<SidebarProject />
|
||||
{:else}
|
||||
<SidebarOrganization organization={$page.data.currentOrganization} />
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</main>
|
||||
|
||||
<style lang="scss">
|
||||
main {
|
||||
min-height: 100vh;
|
||||
background-color: var(--bgcolor-neutral-default);
|
||||
}
|
||||
|
||||
header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
padding: var(--space-4);
|
||||
}
|
||||
|
||||
.studio-content {
|
||||
min-height: calc(100vh - 48px);
|
||||
margin-top: 48px;
|
||||
width: calc(100vw - 200px);
|
||||
margin-left: 200px;
|
||||
padding-right: var(--space-4);
|
||||
|
||||
&.project-sidebar {
|
||||
width: calc(100vw - 52px);
|
||||
margin-left: 52px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,70 +0,0 @@
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import type { Plan } from '$lib/sdk/billing';
|
||||
import type { Tier } from '$lib/stores/billing';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { isCloud } from '$lib/system';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import { Query } from '@appwrite.io/console';
|
||||
|
||||
export const load: LayoutLoad = async ({ params, fetch, depends, parent }) => {
|
||||
await parent();
|
||||
depends(Dependencies.RUNTIMES);
|
||||
depends(Dependencies.CONSOLE_VARIABLES);
|
||||
depends(Dependencies.ORGANIZATION);
|
||||
|
||||
const prefs = await sdk.forConsole.account.getPrefs();
|
||||
|
||||
const { endpoint, project } = sdk.forConsole.client.config;
|
||||
const versionPromise = fetch(`${endpoint}/health/version`, {
|
||||
headers: {
|
||||
'X-Appwrite-Project': project
|
||||
}
|
||||
}).then((response) => response.json() as { version?: string });
|
||||
|
||||
const variablesPromise = sdk.forConsole.console.variables();
|
||||
|
||||
const [data, variables] = await Promise.all([versionPromise, variablesPromise]);
|
||||
|
||||
let plansInfo = new Map<Tier, Plan>();
|
||||
if (isCloud) {
|
||||
const plansArray = await sdk.forConsole.billing.getPlansInfo();
|
||||
plansInfo = plansArray.plans.reduce((map, plan) => {
|
||||
map.set(plan.$id as Tier, plan);
|
||||
return map;
|
||||
}, new Map<Tier, Plan>());
|
||||
}
|
||||
|
||||
const organizations = !isCloud
|
||||
? await sdk.forConsole.teams.list()
|
||||
: await sdk.forConsole.billing.listOrganization();
|
||||
|
||||
let projects = [];
|
||||
let currentOrgId = params.organization ? params.organization : prefs.organization;
|
||||
|
||||
if (!currentOrgId && organizations.teams.length > 0) {
|
||||
currentOrgId = organizations.teams[0].$id;
|
||||
}
|
||||
if (currentOrgId) {
|
||||
const orgProjects = await sdk.forConsole.projects.list([
|
||||
Query.equal('teamId', currentOrgId),
|
||||
Query.limit(100),
|
||||
Query.orderDesc('$updatedAt')
|
||||
]);
|
||||
projects = orgProjects.projects.length > 0 ? orgProjects.projects : [];
|
||||
}
|
||||
|
||||
const selectedOrganizationId =
|
||||
params && params.organization ? params.organization : prefs.organization;
|
||||
|
||||
return {
|
||||
consoleVariables: variables,
|
||||
version: data?.version ?? null,
|
||||
plansInfo,
|
||||
roles: [],
|
||||
scopes: [],
|
||||
projects: projects,
|
||||
currentProjectId: params.project ?? '',
|
||||
organizations: organizations,
|
||||
currentOrganization: organizations.teams.find((org) => org.$id === selectedOrganizationId)
|
||||
};
|
||||
};
|
||||
@@ -1,30 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Card, Typography, Layout, Divider, Button } from '@appwrite.io/pink-svelte';
|
||||
import { base } from '$app/paths';
|
||||
|
||||
export let data;
|
||||
</script>
|
||||
|
||||
<Card.Base>
|
||||
<Layout.Stack>
|
||||
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Typography.Text variant="m-500" color="--fgcolor-neutral-secondary"
|
||||
>Projects</Typography.Text>
|
||||
<Button.Button size="s">Create project</Button.Button>
|
||||
</Layout.Stack>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Layout.Grid gap="l" columnsS={3} columns={4} columnsL={4} columnsXL={5}>
|
||||
{#each data.projects as project}
|
||||
<a href={`${base}/proj-${project.$id}`}
|
||||
><Card.Media
|
||||
src="https://picsum.photos/260/150"
|
||||
alt=""
|
||||
description={project.description}
|
||||
title={project.name}></Card.Media
|
||||
></a>
|
||||
{/each}
|
||||
</Layout.Grid>
|
||||
</Layout.Stack>
|
||||
</Card.Base>
|
||||
@@ -1,30 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Layout, Card, Typography, Divider } from '@appwrite.io/pink-svelte';
|
||||
</script>
|
||||
|
||||
<Layout.Stack direction="row">
|
||||
<section class="chat">
|
||||
<Card.Base>
|
||||
<Layout.Stack height="calc(100vh - 120px)">
|
||||
<Typography.Text>Chat</Typography.Text>
|
||||
<Divider />
|
||||
<Typography.Text>
|
||||
Phasellus a ultricies nulla. Proin tristique aliquam dui. Phasellus porta
|
||||
sodales justo, et feugiat ligula pulvinar sed. Morbi et nulla hendrerit,
|
||||
pellentesque est sodales, euismod magna. Suspendisse vel massa arcu. Nulla
|
||||
ultrices sit amet metus ac euismod. Curabitur ultrices aliquet libero, a
|
||||
pharetra orci tristique in. Etiam egestas fermentum justo ut semper. Sed
|
||||
vulputate eros erat, ac sollicitudin nulla condimentum volutpat. Sed vulputate
|
||||
est ante.
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
</Card.Base>
|
||||
</section>
|
||||
<Card.Base>Iframe</Card.Base>
|
||||
</Layout.Stack>
|
||||
|
||||
<style>
|
||||
.chat {
|
||||
width: 600px;
|
||||
}
|
||||
</style>
|
||||
+2
-3
@@ -41,9 +41,8 @@ export const load: PageLoad = async ({ parent, url }) => {
|
||||
} else {
|
||||
redirect(
|
||||
303,
|
||||
isStudio
|
||||
? `${base}/org-${teamId}${url.search}`
|
||||
: `${base}/organization-${teamId}${url.search}`
|
||||
|
||||
`${base}/organization-${teamId}${url.search}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user