Merge pull request #2724 from appwrite/optimize-projects-list

This commit is contained in:
Darshan
2025-12-22 17:51:07 +05:30
committed by GitHub
22 changed files with 171 additions and 109 deletions
+5 -1
View File
@@ -38,7 +38,11 @@ export const projectsSearcher = (async (query: string) => {
}
const { projects } = await sdk.forConsole.projects.list({
queries: [Query.equal('teamId', get(organization).$id), Query.orderDesc('')]
queries: [
Query.equal('teamId', get(organization).$id),
Query.orderDesc(''),
Query.select(['$id', 'name', 'region'])
]
});
return projects
+2 -1
View File
@@ -140,7 +140,8 @@
queries: [
Query.equal('teamId', organizationId ?? page.data.currentOrgId),
Query.limit(5),
Query.orderDesc('$updatedAt')
Query.orderDesc('$updatedAt'),
Query.select(['$id', 'name', 'region', 'teamId'])
]
})) ?? loadedProjects;
-1
View File
@@ -63,7 +63,6 @@ export enum Dependencies {
DEPLOYMENTS = 'dependency:deployments',
EXECUTIONS = 'dependency:executions',
PLATFORM = 'dependency:platform',
PLATFORMS = 'dependency:platforms',
KEY = 'dependency:key',
KEYS = 'dependency:keys',
DEV_KEY = 'dependency:dev_key',
+5 -1
View File
@@ -35,7 +35,11 @@ export const load: LayoutLoad = async ({ depends, parent }) => {
try {
projectsCount = (
await sdk.forConsole.projects.list({
queries: [Query.equal('teamId', currentOrgId), Query.limit(1)]
queries: [
Query.equal('teamId', currentOrgId),
Query.limit(1),
Query.select(['$id'])
]
})
).total;
} catch (e) {
@@ -65,7 +65,7 @@ export const load: PageLoad = async ({ parent }) => {
let projects: Models.ProjectList = null;
try {
projects = await sdk.forConsole.projects.list({
queries: [Query.equal('teamId', org.$id), Query.limit(1)]
queries: [Query.equal('teamId', org.$id), Query.limit(1), Query.select(['$id'])]
});
} catch (e) {
redirect(303, `${base}/organization-${org.$id}`);
@@ -5,6 +5,7 @@ import { CARD_LIMIT, Dependencies } from '$lib/constants';
import type { PageLoad } from './$types';
import { redirect } from '@sveltejs/kit';
import { base } from '$app/paths';
import { isCloud } from '$lib/system';
export const load: PageLoad = async ({ params, url, route, depends, parent }) => {
const { scopes } = await parent();
@@ -24,7 +25,8 @@ export const load: PageLoad = async ({ params, url, route, depends, parent }) =>
Query.offset(offset),
Query.equal('teamId', params.organization),
Query.limit(limit),
Query.orderDesc('')
Query.orderDesc(''),
Query.select(['$id', 'name', 'platforms', 'region', ...(isCloud ? ['status'] : [])])
],
search: search || undefined
});
@@ -106,10 +106,13 @@
}
try {
allProjects = await sdk.forConsole.projects.list([
Query.equal('teamId', data.organization.$id),
Query.limit(1000)
]);
allProjects = await sdk.forConsole.projects.list({
queries: [
Query.equal('teamId', data.organization.$id),
Query.limit(1000),
Query.select(['$id', 'name'])
]
});
} catch {
allProjects = { projects: [] };
}
@@ -9,7 +9,9 @@ export const load: PageLoad = async ({ depends, params, parent }) => {
depends(Dependencies.ORGANIZATION);
const [projects, invoices] = await Promise.all([
sdk.forConsole.projects.list({ queries: [Query.equal('teamId', params.organization)] }),
sdk.forConsole.projects.list({
queries: [Query.equal('teamId', params.organization), Query.select(['$id', 'name'])]
}),
isCloud ? sdk.forConsole.billing.listInvoices(params.organization) : undefined
]);
@@ -19,8 +19,8 @@
export let showDelete = false;
export let invoices: InvoiceList;
let error: string = null;
let error: string = null;
let selectedTab = 'projects';
let organizationName: string = null;
let estimation: EstimationDeleteOrganization;
@@ -1,7 +1,7 @@
import { sdk } from '$lib/stores/sdk';
import type { PageLoad } from './$types';
import { Query } from '@appwrite.io/console';
import type { UsageProjectInfo } from '../../store';
import { type Models, Query } from '@appwrite.io/console';
import type { Invoice, OrganizationUsage } from '$lib/sdk/billing';
export const load: PageLoad = async ({ params, parent }) => {
@@ -70,14 +70,18 @@ export const load: PageLoad = async ({ params, parent }) => {
// all this to get the project's name and region!
function getUsageProjects(usage: OrganizationUsage) {
return (async () => {
const projects: Record<string, UsageProjectInfo> = {};
const limit = 100;
const requests = [];
const requests: Array<Promise<Models.ProjectList>> = [];
const projects: Record<string, UsageProjectInfo> = {};
for (let index = 0; index < usage.projects.length; index += limit) {
const chunkIds = usage.projects.slice(index, index + limit).map((p) => p.projectId);
requests.push(
sdk.forConsole.projects.list({
queries: [Query.limit(limit), Query.equal('$id', chunkIds)]
queries: [
Query.limit(limit),
Query.equal('$id', chunkIds),
Query.select(['$id', 'name', 'region'])
]
})
);
}
@@ -1,4 +1,4 @@
<script lang="ts" context="module">
<script lang="ts" module>
import { columns } from './store';
import { wizard } from '$lib/stores/wizard';
import CreateAndroid from './createAndroid.svelte';
@@ -59,8 +59,7 @@
</script>
<script lang="ts">
import { base } from '$app/paths';
import { page } from '$app/state';
import { resolve } from '$app/paths';
import { Button } from '$lib/elements/forms';
import { canWritePlatforms } from '$lib/stores/roles';
import { setOverviewAction } from '../context';
@@ -84,25 +83,33 @@
import type { ComponentType } from 'svelte';
import DualTimeView from '$lib/components/dualTimeView.svelte';
export let data;
import { page } from '$app/state';
import type { PageProps } from './$types';
import type { Models } from '@appwrite.io/console';
import { type DeleteOperationState, MultiSelectionTable } from '$lib/components';
import { sdk } from '$lib/stores/sdk';
import { Submit, trackError } from '$lib/actions/analytics';
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
enum PlatformTypes {
'apple-ios' = 'iOS',
'apple-macos' = 'macOS',
'apple-watchos' = 'watchOS',
'apple-tvos' = 'tvOS',
'android' = 'Android',
'flutter-android' = 'Android',
'flutter-ios' = 'iOS',
'flutter-linux' = 'Linux',
'flutter-macos' = 'macOS',
'flutter-windows' = 'Windows',
'flutter-web' = 'Web',
'react-native-android' = 'Android',
'react-native-ios' = 'iOS',
'web' = 'Web'
}
const path = `${base}/project-${page.params.region}-${page.params.project}/overview/platforms`;
const { data }: PageProps = $props();
const PlatformTypes = {
'apple-ios': 'iOS',
'apple-macos': 'macOS',
'apple-watchos': 'watchOS',
'apple-tvos': 'tvOS',
android: 'Android',
'flutter-android': 'Android',
'flutter-ios': 'iOS',
'flutter-linux': 'Linux',
'flutter-macos': 'macOS',
'flutter-windows': 'Windows',
'flutter-web': 'Web',
'react-native-android': 'Android',
'react-native-ios': 'iOS',
web: 'Web'
} as const;
function getPlatformInfo(platform: string): ComponentType {
if (platform.includes('flutter')) {
@@ -118,46 +125,82 @@
}
}
function getPlatformPath(platform: Models.Platform) {
return resolve('/(console)/project-[region]-[project]/overview/platforms/[platform]', {
region: page.params.region,
project: page.params.project,
platform: platform.$id
});
}
async function handlePlatformDelete(
selectedPlatforms: string[]
): Promise<DeleteOperationState> {
const promises = selectedPlatforms.map((platformId) => {
return sdk.forConsole.projects.deletePlatform({
projectId: page.params.project,
platformId
});
});
try {
await Promise.all(promises);
trackEvent(Submit.PlatformDelete, { total: selectedPlatforms.length });
} catch (error) {
trackError(error, Submit.PlatformDelete);
return error;
} finally {
await invalidate(Dependencies.PROJECT);
}
}
setOverviewAction(Action);
</script>
{#if data.platforms.platforms.length}
<Table.Root columns={$columns} let:root>
<svelte:fragment slot="header" let:root>
{#if data.platforms.total}
<MultiSelectionTable
resource="platform"
columns={$columns}
onDelete={handlePlatformDelete}
allowSelection={false}>
{#snippet header(root)}
{#each $columns as column}
<Table.Header.Cell {root} column={column.id}>
{column.title}
</Table.Header.Cell>
{/each}
</svelte:fragment>
{#each data.platforms.platforms as platform}
<Table.Row.Link href={`${path}/${platform.$id}`} {root}>
<Table.Cell {root}>
{platform.name}
</Table.Cell>
<Table.Cell {root}>
<Layout.Stack direction="row" gap="s" alignItems="center">
<Icon icon={getPlatformInfo(platform.type)} />
{PlatformTypes[platform.type]}
</Layout.Stack>
</Table.Cell>
<Table.Cell {root}>
{#if platform.type.includes('web') || platform.type === 'web'}
{platform.hostname || '—'}
{:else}
{platform.key || platform.hostname || '—'}
{/if}
</Table.Cell>
<Table.Cell {root}>
{#if platform.$updatedAt}
<DualTimeView time={platform.$updatedAt} />
{:else}
never
{/if}
</Table.Cell>
</Table.Row.Link>
{/each}
</Table.Root>
{/snippet}
{#snippet children(root)}
{#each data.platforms.platforms as platform}
<Table.Row.Link href={getPlatformPath(platform)} {root} id={platform.$id}>
<Table.Cell {root}>
{platform.name}
</Table.Cell>
<Table.Cell {root}>
<Layout.Stack direction="row" gap="s" alignItems="center">
<Icon icon={getPlatformInfo(platform.type)} />
{PlatformTypes[platform.type]}
</Layout.Stack>
</Table.Cell>
<Table.Cell {root}>
{#if platform.type.includes('web') || platform.type === 'web'}
{platform.hostname || '—'}
{:else}
{platform.key || platform.hostname || '—'}
{/if}
</Table.Cell>
<Table.Cell {root}>
{#if platform.$updatedAt}
<DualTimeView time={platform.$updatedAt} />
{:else}
never
{/if}
</Table.Cell>
</Table.Row.Link>
{/each}
{/snippet}
</MultiSelectionTable>
{:else}
<Card.Base padding="none">
<Empty
@@ -1,10 +1,11 @@
import { sdk } from '$lib/stores/sdk';
import type { PageLoad } from './$types';
import { Dependencies } from '$lib/constants';
export const load: PageLoad = async ({ params, depends }) => {
depends(Dependencies.PLATFORMS);
export const load: PageLoad = async ({ parent }) => {
const { project } = await parent();
return {
platforms: await sdk.forConsole.projects.listPlatforms({ projectId: params.project })
platforms: {
platforms: project.platforms,
total: project.platforms.length
}
};
};
@@ -17,7 +17,7 @@
projectId: $project.$id,
platformId: $platform.$id
});
await invalidate(Dependencies.PLATFORMS);
await invalidate(Dependencies.PROJECT);
showDelete = false;
addNotification({
type: 'success',
@@ -134,10 +134,7 @@ const val APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.region, page.
message: 'Platform created.'
});
await Promise.all([
invalidate(Dependencies.PROJECT),
invalidate(Dependencies.PLATFORMS)
]);
await invalidate(Dependencies.PROJECT);
} catch (error) {
trackError(error, Submit.PlatformCreate);
addNotification({
@@ -97,10 +97,7 @@ APPWRITE_PUBLIC_ENDPOINT: "${sdk.forProject(page.params.region, page.params.proj
message: 'Platform created.'
});
await Promise.all([
invalidate(Dependencies.PROJECT),
invalidate(Dependencies.PLATFORMS)
]);
await invalidate(Dependencies.PROJECT);
} catch (error) {
trackError(error, Submit.PlatformCreate);
addNotification({
@@ -187,10 +187,7 @@ client.ping();
message: 'Platform created.'
});
await Promise.all([
invalidate(Dependencies.PROJECT),
invalidate(Dependencies.PLATFORMS)
]);
await invalidate(Dependencies.PROJECT);
} catch (error) {
trackError(error, Submit.PlatformCreate);
addNotification({
@@ -128,10 +128,7 @@ EXPO_PUBLIC_APPWRITE_ENDPOINT=${sdk.forProject(page.params.region, page.params.p
message: 'Platform created.'
});
await Promise.all([
invalidate(Dependencies.PROJECT),
invalidate(Dependencies.PLATFORMS)
]);
await invalidate(Dependencies.PROJECT);
} catch (error) {
trackError(error, Submit.PlatformCreate);
addNotification({
@@ -243,10 +243,7 @@ APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.project).c
message: 'Platform created.'
});
await Promise.all([
invalidate(Dependencies.PROJECT),
invalidate(Dependencies.PLATFORMS)
]);
await invalidate(Dependencies.PROJECT);
} catch (error) {
trackError(error, Submit.PlatformCreate);
addNotification({
+3 -1
View File
@@ -80,7 +80,9 @@
onMount(async () => {
// Filter projects by organization ID using server-side queries
const projectList = await sdk.forConsole.projects.list({
queries: $organization?.$id ? [Query.equal('teamId', $organization.$id)] : []
queries: $organization?.$id
? [Query.equal('teamId', $organization.$id), Query.select(['$id', 'name'])]
: []
});
projectOptions = projectList.projects.map((project) => ({
value: project.$id,
@@ -43,7 +43,11 @@
loadingProjects = true;
projects = await sdk.forConsole.projects.list({
queries: [Query.equal('teamId', selectedOrg), Query.orderDesc('')]
queries: [
Query.equal('teamId', selectedOrg),
Query.orderDesc(''),
Query.select(['$id', 'name'])
]
});
selectedProject = projects?.total ? projects.projects[0].$id : null;
@@ -215,9 +219,9 @@
: undefined}
disabled={loadingProjects}
options={[
...(projects?.projects?.map((p) => ({
label: p.name,
value: p.$id
...(projects?.projects?.map((project) => ({
label: project.name,
value: project.$id
})) ?? []),
{
label: 'Create project',
@@ -48,7 +48,11 @@
async function fetchProjects() {
loadingProjects = true;
projects = await sdk.forConsole.projects.list({
queries: [Query.equal('teamId', selectedOrg), Query.orderDesc('')]
queries: [
Query.equal('teamId', selectedOrg),
Query.orderDesc(''),
Query.select(['$id', 'name'])
]
});
selectedProject = projects?.total ? projects.projects[0].$id : null;
@@ -87,7 +91,7 @@
loadingProjects = false;
}
} else {
const project = projects.projects.find((p) => p.$id === selectedProject);
const project = projects.projects.find((project) => project.$id === selectedProject);
if (!project) {
addNotification({ type: 'error', message: 'Selected project not found' });
return;
@@ -371,9 +375,9 @@
: undefined}
disabled={loadingProjects}
options={[
...(projects?.projects?.map((p) => ({
label: p.name,
value: p.$id
...(projects?.projects?.map((project) => ({
label: project.name,
value: project.$id
})) ?? []),
{
label: 'Create project',
@@ -55,7 +55,11 @@
async function fetchProjects() {
projects = await sdk.forConsole.projects.list({
queries: [Query.equal('teamId', selectedOrg), Query.orderDesc('')]
queries: [
Query.equal('teamId', selectedOrg),
Query.orderDesc(''),
Query.select(['$id', 'name', 'region'])
]
});
selectedProject = projects?.total ? projects.projects[0].$id : null;
}
@@ -180,9 +184,9 @@
label="Project"
required
options={[
...projects.projects.map((p) => ({
label: p.name,
value: p.$id
...projects.projects.map((project) => ({
label: project.name,
value: project.$id
})),
{
label: 'Create project',