diff --git a/src/lib/components/billing/selectPaymentMethod.svelte b/src/lib/components/billing/selectPaymentMethod.svelte index 7dfe14a2ed..9a7265c8b7 100644 --- a/src/lib/components/billing/selectPaymentMethod.svelte +++ b/src/lib/components/billing/selectPaymentMethod.svelte @@ -11,9 +11,9 @@ import { invalidate } from '$app/navigation'; import { Dependencies } from '$lib/constants'; - export let methods: PaymentList; export let value: string; export let taxId = ''; + export let methods: PaymentList; let showTaxId = false; let showPaymentModal = false; diff --git a/src/lib/components/bottom-sheet/SheetMenuBlock.svelte b/src/lib/components/bottom-sheet/SheetMenuBlock.svelte index a8d65c4be2..6bc3d2cab9 100644 --- a/src/lib/components/bottom-sheet/SheetMenuBlock.svelte +++ b/src/lib/components/bottom-sheet/SheetMenuBlock.svelte @@ -11,7 +11,7 @@ {#if menu?.title} {menu.title} {/if} - + {#each menu.items as menuItem} {#if menuItem.href} import { createMenubar, melt } from '@melt-ui/svelte'; - import { Badge, Icon, type SheetMenu, ActionMenu, Card } from '@appwrite.io/pink-svelte'; + import { + Badge, + Icon, + type SheetMenu, + Layout, + ActionMenu, + Card, + Skeleton + } from '@appwrite.io/pink-svelte'; import { IconChevronDown, IconChevronRight, @@ -14,20 +22,13 @@ import { base } from '$app/paths'; import { currentPlan, newOrgModal } from '$lib/stores/organization'; import { Click, trackEvent } from '$lib/actions/analytics'; - import { page } from '$app/stores'; + import type { Models } from '@appwrite.io/console'; - type Project = { - name: string; - $id: string; - isSelected: boolean; - region: string; - }; type Organization = { name: string; $id: string; tierName: string; isSelected: boolean; - projects: Array; }; const { @@ -62,13 +63,15 @@ } } = createMenu(); - export let organizations: Organization[] = []; + let isLoadingProjects = true; + let loadedProjects: Models.ProjectList = { total: 0, projects: [] }; - $: selectedOrg = organizations.find((organization) => organization.isSelected); - $: selectedProject = $page.data.project; + export let organizations: Organization[] = []; + export let currentProject: Models.Project | null = null; + export let projects: Promise = Promise.resolve(loadedProjects); - let organisationBottomSheetOpen = false; let projectsBottomSheetOpen = false; + let organisationBottomSheetOpen = false; function createOrg() { trackEvent(Click.OrganizationClickCreate, { source: 'breadcrumbs' }); @@ -96,85 +99,76 @@ } }; - $: organizationsBottomSheet = !selectedOrg - ? switchOrganization - : ({ - top: { - items: [ - { - name: 'Organization overview', - href: `${base}/organization-${selectedOrg?.$id}` - } - ] - }, - bottom: - organizations.length > 1 - ? { - items: [ - { - name: 'Switch organization', - trailingIcon: IconChevronRight, - subMenu: switchOrganization - } - ] - } - : { - items: [ - { - name: 'Create organization', - leadingIcon: IconPlus, - onClick: createOrg - } - ] - } - } satisfies SheetMenu); - - $: projectsBottomSheet = { - top: - selectedOrg?.projects.length > 1 - ? { - title: 'Switch project', - items: !selectedOrg - ? [] - : selectedOrg?.projects - .map((project, index) => { - if (index < 4) { - return { - name: project.name, - href: `${base}/project-${project.region}-${project.$id}/overview` - }; - } else if (index === 4) { - return { - name: 'All projects', - href: `${base}/organization-${selectedOrg?.$id}` - }; - } - return null; - }) - .filter((project) => project !== null) - } - : { + async function createProjectsBottomSheet(organization: Organization): Promise { + isLoadingProjects = true; + loadedProjects = await projects; + isLoadingProjects = false; + + const createProjectItem = { + name: 'Create project', + trailingIcon: IconPlus, + href: `${base}/organization-${organization?.$id}?create-project` + }; + + if (loadedProjects.total > 1 && selectedOrg) { + const projectLinks = loadedProjects.projects.slice(0, 4).map((project) => ({ + name: project.name, + href: `${base}/project-${project.region}-${project.$id}/overview/platforms` + })); + + if (loadedProjects.projects.length > 4) { + projectLinks.push({ + name: 'All projects', + href: `${base}/organization-${selectedOrg.$id}` + }); + } + + return { + top: { title: 'Switch project', items: projectLinks }, + bottom: { items: [createProjectItem] } + }; + } + + return { + top: { items: [createProjectItem] }, + bottom: { items: [createProjectItem] } + }; + } + + function createOrganizationBottomSheet(organization: Organization) { + return !organization + ? switchOrganization + : ({ + top: { items: [ { - name: 'Create project', - trailingIcon: IconPlus, - href: `${base}/organization-${selectedOrg?.$id}?create-project` + name: 'Organization overview', + href: `${base}/organization-${organization?.$id}` } ] }, - bottom: - selectedOrg?.projects.length > 1 - ? { - items: [ - { - name: 'Create project', - trailingIcon: IconPlus, - href: `${base}/organization-${selectedOrg?.$id}?create-project` - } - ] - } - : undefined - } satisfies SheetMenu; + bottom: + organizations.length > 1 + ? { + items: [ + { + name: 'Switch organization', + trailingIcon: IconChevronRight, + subMenu: switchOrganization + } + ] + } + : { + items: [ + { + name: 'Create organization', + leadingIcon: IconPlus, + onClick: createOrg + } + ] + } + } satisfies SheetMenu); + } function onResize() { if ((organisationBottomSheetOpen || projectsBottomSheetOpen) && !$isSmallViewport) { @@ -183,6 +177,12 @@ } } + $: selectedOrg = organizations.find((org) => org.isSelected); + + $: projectsBottomSheet = createProjectsBottomSheet(selectedOrg); + + $: organizationsBottomSheet = createOrganizationBottomSheet(selectedOrg); + $: correctPlanName = // the plan names are hardcoded in some cases and are not available locally, // so we rely on the plan's source of truth - `$currentPlan` @@ -216,7 +216,7 @@ organisationBottomSheetOpen = true; }} aria-label="Open organizations tab"> - {selectedOrg?.name ?? 'Organization'} @@ -296,7 +296,7 @@ - {#if selectedOrg && selectedProject} + {#if selectedOrg && currentProject} / {#if !$isSmallViewport} {:else} @@ -313,20 +313,28 @@ class="trigger" on:click={() => (projectsBottomSheetOpen = true)} aria-label="Open projects tab"> - {selectedProject.name} + {currentProject.name} {/if} + - + +{#await projectsBottomSheet then menu} + +{/await}