mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge pull request #2724 from appwrite/optimize-projects-list
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
]);
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
projectId: $project.$id,
|
||||
platformId: $platform.$id
|
||||
});
|
||||
await invalidate(Dependencies.PLATFORMS);
|
||||
await invalidate(Dependencies.PROJECT);
|
||||
showDelete = false;
|
||||
addNotification({
|
||||
type: 'success',
|
||||
|
||||
+1
-4
@@ -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({
|
||||
|
||||
+1
-4
@@ -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({
|
||||
|
||||
+1
-4
@@ -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({
|
||||
|
||||
+1
-4
@@ -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({
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user