Move to reuse existing routing

This commit is contained in:
ernstmul
2025-04-02 11:33:19 +02:00
parent a25aad1040
commit 22bd0e2189
7 changed files with 241 additions and 129 deletions
+12 -12
View File
@@ -80,7 +80,7 @@
title: 'Switch Organization',
items: organizations.map((organization) => ({
name: organization.name,
href: `/console/organization-${organization?.$id}`
href: `${base}/organization-${organization?.$id}`
}))
},
bottom: {
@@ -101,7 +101,7 @@
items: [
{
name: 'Organization overview',
href: `/console/organization-${selectedOrg?.$id}`
href: `${base}/organization-${selectedOrg?.$id}`
}
]
},
@@ -139,12 +139,12 @@
if (index < 4) {
return {
name: project.name,
href: `/console/project-${project.$id}/overview`
href: `${base}/project-${project.$id}/overview`
};
} else if (index === 4) {
return {
name: 'All projects',
href: `/console/organization-${selectedOrg?.$id}`
href: `${base}/organization-${selectedOrg?.$id}`
};
}
return null;
@@ -156,7 +156,7 @@
{
name: 'Create project',
trailingIcon: IconPlus,
href: `/console/organization-${selectedOrg?.$id}?create-project`
href: `${base}/organization-${selectedOrg?.$id}?create-project`
}
]
},
@@ -167,7 +167,7 @@
{
name: 'Create project',
trailingIcon: IconPlus,
href: `/console/organization-${selectedOrg?.$id}?create-project`
href: `${base}/organization-${selectedOrg?.$id}?create-project`
}
]
}
@@ -221,7 +221,7 @@
{#if selectedOrg}
<div use:melt={$itemOrganizations}>
<ActionMenu.Root>
<ActionMenu.Item.Anchor href={`/console/organization-${selectedOrg?.$id}`}
<ActionMenu.Item.Anchor href={`${base}/organization-${selectedOrg?.$id}`}
>Organization overview</ActionMenu.Item.Anchor
></ActionMenu.Root>
</div>
@@ -241,7 +241,7 @@
<div use:melt={$itemOrganizations}>
<ActionMenu.Root>
<ActionMenu.Item.Anchor
href={`/console/organization-${organization?.$id}`}
href={`${base}/organization-${organization?.$id}`}
>{organization.name}</ActionMenu.Item.Anchor>
</ActionMenu.Root>
</div>
@@ -272,7 +272,7 @@
<div use:melt={$itemOrganizations}>
<ActionMenu.Root>
<ActionMenu.Item.Anchor
href={`/console/organization-${organization?.$id}`}
href={`${base}/organization-${organization?.$id}`}
>{organization.name}</ActionMenu.Item.Anchor
></ActionMenu.Root>
</div>
@@ -317,7 +317,7 @@
{#if index < 4}
<div use:melt={$itemProjects}>
<ActionMenu.Root>
<ActionMenu.Item.Anchor href={`/console/project-${project.$id}`}
<ActionMenu.Item.Anchor href={`${base}/project-${project.$id}`}
>{project.name}</ActionMenu.Item.Anchor
></ActionMenu.Root>
</div>
@@ -325,7 +325,7 @@
<div use:melt={$itemProjects}>
<ActionMenu.Root>
<ActionMenu.Item.Anchor
href={`/console/organization-${selectedOrg.$id}`}
href={`${base}/organization-${selectedOrg.$id}`}
>All projects</ActionMenu.Item.Anchor
></ActionMenu.Root>
</div>
@@ -337,7 +337,7 @@
<ActionMenu.Root>
<ActionMenu.Item.Anchor
leadingIcon={IconPlusSm}
href={`/console/organization-${selectedOrg?.$id}?create-project`}
href={`${base}/organization-${selectedOrg?.$id}?create-project`}
>Create project</ActionMenu.Item.Anchor
></ActionMenu.Root>
</div>
@@ -11,15 +11,15 @@
<Typography.Text color="--fgcolor-neutral-tertiary">{organization.name}</Typography.Text>
<Layout.Stack gap="xs">
<ActionMenu.Item.Anchor href={`${base}/org-${organization.$id}`}
<ActionMenu.Item.Anchor href={`${base}/organization-${organization.$id}`}
>Projects</ActionMenu.Item.Anchor>
<ActionMenu.Item.Anchor href={`${base}/org-${organization.$id}/members`}
<ActionMenu.Item.Anchor href={`${base}/organization-${organization.$id}/members`}
>Members</ActionMenu.Item.Anchor>
<ActionMenu.Item.Anchor href={`${base}/org-${organization.$id}/usage`}
<ActionMenu.Item.Anchor href={`${base}/organization-${organization.$id}/usage`}
>Usage</ActionMenu.Item.Anchor>
<ActionMenu.Item.Anchor href={`${base}/org-${organization.$id}/billing`}
<ActionMenu.Item.Anchor href={`${base}/organization-${organization.$id}/billing`}
>Billing</ActionMenu.Item.Anchor>
<ActionMenu.Item.Anchor href={`${base}/org-${organization.$id}/settings`}
<ActionMenu.Item.Anchor href={`${base}/organization-${organization.$id}/settings`}
>Settings</ActionMenu.Item.Anchor>
</Layout.Stack>
</Layout.Stack>
+11 -6
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { Layout } from '@appwrite.io/pink-svelte';
import { isStudio } from '$lib/system';
export let overlapCover = false;
export let size: 'small' | 'medium' | 'large' | 'xl' | 'xxl' | 'xxxl' = null;
@@ -8,13 +9,17 @@
: '';
</script>
<div style:container-type="inline-size" class:overlap-cover={overlapCover}>
<div class="console-container" {style}>
<Layout.Stack gap="l">
<slot />
</Layout.Stack>
{#if isStudio}
<slot />
{:else}
<div style:container-type="inline-size" class:overlap-cover={overlapCover}>
<div class="console-container" {style}>
<Layout.Stack gap="l">
<slot />
</Layout.Stack>
</div>
</div>
</div>
{/if}
<style>
.overlap-cover {
+75
View File
@@ -0,0 +1,75 @@
<script lang="ts">
import { AvatarInitials, Breadcrumbs } from '$lib/components/index.js';
import { Avatar, Layout } from '@appwrite.io/pink-svelte';
import { page } from '$app/stores';
import { organization, organizationList } from '$lib/stores/organization';
import { user } from '$lib/stores/user';
import SidebarProject from '$lib/components/studio/sidebarProject.svelte';
import SidebarOrganization from '$lib/components/studio/sidebarOrganization.svelte';
import { BillingPlan } from '$lib/constants.js';
import { isCloud } from '$lib/system.js';
import { tierToPlan } from '$lib/stores/billing.js';
import type { NavbarProject } from '$lib/components/navbar.svelte';
$: hasProjectSidebar = $page.url.pathname.startsWith('/studio/proj');
export let loadedProjects: Array<NavbarProject> = [];
$: 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.organization?.$id === org.$id,
projects: loadedProjects
};
});
$: console.log(organizations);
</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.organization} />
{/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>
+20 -16
View File
@@ -3,6 +3,7 @@
import { BillingPlan, INTERVAL } from '$lib/constants';
import Footer from '$lib/layout/footer.svelte';
import Shell from '$lib/layout/shell.svelte';
import ShellStudio from '$lib/layout/shellStudio.svelte';
import { app } from '$lib/stores/app';
import { newOrgModal, organization, type Organization } from '$lib/stores/organization';
import { database, checkForDatabaseBackupPolicies } from '$lib/stores/database';
@@ -32,7 +33,7 @@
import { openMigrationWizard } from './(migration-wizard)';
import { project } from './project-[project]/store';
import { feedback } from '$lib/stores/feedback';
import { hasStripePublicKey, isCloud, VARS } from '$lib/system';
import { hasStripePublicKey, isCloud, isStudio, VARS } from '$lib/system';
import { stripe } from '$lib/stores/stripe';
import MobileSupportModal from './wizard/support/mobileSupportModal.svelte';
import { showSupportModal } from './wizard/support/store';
@@ -329,21 +330,24 @@
</script>
<CommandCenter />
<Shell
showSideNavigation={$page.url.pathname !== '/console' &&
!$page?.params.organization &&
!$page.url.pathname.includes('/console/account') &&
!$page.url.pathname.includes('/console/card') &&
!$page.url.pathname.includes('/console/onboarding')}
showHeader={!$page.url.pathname.includes('/console/onboarding')}
showFooter={!$page.url.pathname.includes('/console/onboarding')}
bind:loadedProjects
bind:projects={data.projects}>
<!-- <Header slot="header" />-->
<slot />
<Footer slot="footer" />
</Shell>
{#if isStudio}
<ShellStudio bind:loadedProjects><slot /></ShellStudio>
{:else}
<Shell
showSideNavigation={$page.url.pathname !== '/console' &&
!$page?.params.organization &&
!$page.url.pathname.includes('/console/account') &&
!$page.url.pathname.includes('/console/card') &&
!$page.url.pathname.includes('/console/onboarding')}
showHeader={!$page.url.pathname.includes('/console/onboarding')}
showFooter={!$page.url.pathname.includes('/console/onboarding')}
bind:loadedProjects
bind:projects={data.projects}>
<!-- <Header slot="header" />-->
<slot />
<Footer slot="footer" />
</Shell>
{/if}
{#if $wizard.show && $wizard.component}
<svelte:component this={$wizard.component} {...$wizard.props} />
{:else if $wizard.cover}
@@ -4,7 +4,7 @@
import { Container } from '$lib/layout';
import CreateProject from './createProject.svelte';
import CreateOrganization from '../createOrganization.svelte';
import { GRACE_PERIOD_OVERRIDE, isCloud } from '$lib/system';
import { GRACE_PERIOD_OVERRIDE, isCloud, isStudio } from '$lib/system';
import { page } from '$app/stores';
import { registerCommands } from '$lib/commandCenter';
import {
@@ -28,7 +28,7 @@
import { onMount, type ComponentType } from 'svelte';
import { canWriteProjects } from '$lib/stores/roles';
import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect';
import { Badge, Icon, Typography } from '@appwrite.io/pink-svelte';
import { Badge, Divider, Icon, Typography, Card, Layout } from '@appwrite.io/pink-svelte';
import {
IconAndroid,
IconApple,
@@ -48,6 +48,7 @@
let showCreateProjectCloud = false;
function allServiceDisabled(project: Models.Project): boolean {
return false;
let disabled = true;
services.load(project);
$services.list.forEach((service) => {
@@ -142,94 +143,124 @@
}
</script>
<Container>
<div class="u-flex u-gap-12 common-section u-main-space-between">
<Typography.Title>Projects</Typography.Title>
{#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>
<DropList bind:show={showDropdown} placement="bottom-end">
{#if $canWriteProjects}
<Button
on:click={handleCreateProject}
event="create_project"
disabled={$readOnly && !GRACE_PERIOD_OVERRIDE}>
<Icon icon={IconPlus} slot="start" size="s" />
Create project
</Button>
{/if}
<svelte:fragment slot="list">
<DropListItem on:click={() => (showCreate = true)}>Empty project</DropListItem>
<DropListItem on:click={importProject}>
<div class="u-flex u-gap-8 u-cross-center">
Import project <span class="tag eyebrow-heading-3">Experimental</span>
</div>
</DropListItem>
</svelte:fragment>
</DropList>
</div>
<Divider />
{#if data.projects.total}
<CardContainer
showEmpty={$canWriteProjects}
total={data.projects.total}
<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>
</Card.Base>
{:else}
<Container>
<div class="u-flex u-gap-12 common-section u-main-space-between">
<Typography.Title>Projects</Typography.Title>
<DropList bind:show={showDropdown} placement="bottom-end">
{#if $canWriteProjects}
<Button
on:click={handleCreateProject}
event="create_project"
disabled={$readOnly && !GRACE_PERIOD_OVERRIDE}>
<Icon icon={IconPlus} slot="start" size="s" />
Create project
</Button>
{/if}
<svelte:fragment slot="list">
<DropListItem on:click={() => (showCreate = true)}>Empty project</DropListItem>
<DropListItem on:click={importProject}>
<div class="u-flex u-gap-8 u-cross-center">
Import project <span class="tag eyebrow-heading-3">Experimental</span>
</div>
</DropListItem>
</svelte:fragment>
</DropList>
</div>
{#if data.projects.total}
<CardContainer
showEmpty={$canWriteProjects}
total={data.projects.total}
offset={data.offset}
on:click={handleCreateProject}>
{#each data.projects.projects as project}
{@const platforms = filterPlatforms(
project.platforms.map((platform) => getPlatformInfo(platform.type))
)}
<GridItem1 href={`${base}/project-${project.$id}`}>
<svelte:fragment slot="eyebrow">
{project?.platforms?.length ? project?.platforms?.length : 'No'} apps
</svelte:fragment>
<svelte:fragment slot="title">
{project.name}
</svelte:fragment>
{#if allServiceDisabled(project)}
<p>
<span class="icon-pause" aria-hidden="true"></span> All services are
disabled.
</p>
{/if}
{#each platforms as platform, i}
{#if i < 3}
{@const icon = getIconForPlatform(platform.icon)}
<Badge variant="secondary" content={platform.name}>
<Icon {icon} size="s" slot="start" />
</Badge>
{/if}
{/each}
{#if platforms?.length > 3}
<Badge
variant="secondary"
content={`+${project.platforms.length - 3}`} />
{/if}
<svelte:fragment slot="icons">
{#if isCloud && regions}
{@const region = findRegion(project)}
<span class="u-color-text-gray u-medium u-line-height-2">
{region?.name}
</span>
{/if}
</svelte:fragment>
</GridItem1>
{/each}
<svelte:fragment slot="empty">
<p>Create a new project</p>
</svelte:fragment>
</CardContainer>
{:else}
<Empty
single
allowCreate={$canWriteProjects}
on:click={handleCreateProject}
target="project"
href="https://appwrite.io/docs/quick-starts"></Empty>
{/if}
<PaginationWithLimit
name="Projects"
limit={data.limit}
offset={data.offset}
on:click={handleCreateProject}>
{#each data.projects.projects as project}
{@const platforms = filterPlatforms(
project.platforms.map((platform) => getPlatformInfo(platform.type))
)}
<GridItem1 href={`${base}/project-${project.$id}`}>
<svelte:fragment slot="eyebrow">
{project?.platforms?.length ? project?.platforms?.length : 'No'} apps
</svelte:fragment>
<svelte:fragment slot="title">
{project.name}
</svelte:fragment>
{#if allServiceDisabled(project)}
<p>
<span class="icon-pause" aria-hidden="true"></span> All services are disabled.
</p>
{/if}
{#each platforms as platform, i}
{#if i < 3}
{@const icon = getIconForPlatform(platform.icon)}
<Badge variant="secondary" content={platform.name}>
<Icon {icon} size="s" slot="start" />
</Badge>
{/if}
{/each}
{#if platforms?.length > 3}
<Badge variant="secondary" content={`+${project.platforms.length - 3}`} />
{/if}
<svelte:fragment slot="icons">
{#if isCloud && regions}
{@const region = findRegion(project)}
<span class="u-color-text-gray u-medium u-line-height-2">
{region?.name}
</span>
{/if}
</svelte:fragment>
</GridItem1>
{/each}
<svelte:fragment slot="empty">
<p>Create a new project</p>
</svelte:fragment>
</CardContainer>
{:else}
<Empty
single
allowCreate={$canWriteProjects}
on:click={handleCreateProject}
target="project"
href="https://appwrite.io/docs/quick-starts"></Empty>
{/if}
<PaginationWithLimit
name="Projects"
limit={data.limit}
offset={data.offset}
total={data.projects.total} />
</Container>
total={data.projects.total} />
</Container>
{/if}
<CreateOrganization bind:show={addOrganization} />
<CreateProject bind:show={showCreate} teamId={$page.params.organization} />
-3
View File
@@ -11,7 +11,6 @@
import SidebarProject from '$lib/components/studio/sidebarProject.svelte';
$: hasProjectSidebar = $page.url.pathname.startsWith('/studio/proj');
$: console.log('hasProjectSidebar', hasProjectSidebar);
$: loadedProjects = $page.data.projects.map((project) => {
return {
@@ -34,8 +33,6 @@
projects: loadedProjects
};
});
$: console.log('organizations', organizations);
</script>
<main>