mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
feat: user searcher
This commit is contained in:
@@ -1,24 +1,28 @@
|
||||
import { debounce } from '$lib/helpers/debounce';
|
||||
import { isMac } from '$lib/helpers/platform';
|
||||
import { onMount } from 'svelte';
|
||||
import { derived, writable, type Updater } from 'svelte/store';
|
||||
|
||||
// Stores
|
||||
export type CommandGroup =
|
||||
| 'ungrouped'
|
||||
| 'navigation'
|
||||
| 'projects'
|
||||
| 'organizations'
|
||||
| 'auth'
|
||||
| 'help'
|
||||
| 'account'
|
||||
| 'platforms'
|
||||
| 'databases'
|
||||
| 'functions'
|
||||
| 'storage'
|
||||
| 'domains'
|
||||
| 'webhooks'
|
||||
| 'integrations'
|
||||
| 'migrations';
|
||||
const groups = [
|
||||
'ungrouped',
|
||||
'navigation',
|
||||
'projects',
|
||||
'organizations',
|
||||
'auth',
|
||||
'help',
|
||||
'account',
|
||||
'platforms',
|
||||
'databases',
|
||||
'functions',
|
||||
'storage',
|
||||
'domains',
|
||||
'webhooks',
|
||||
'integrations',
|
||||
'migrations',
|
||||
'users'
|
||||
] as const;
|
||||
|
||||
export type CommandGroup = (typeof groups)[number];
|
||||
|
||||
type BaseCommand = {
|
||||
callback: () => void;
|
||||
@@ -251,24 +255,60 @@ export const updateCommandGroupRanks = {
|
||||
};
|
||||
|
||||
export const commandGroupRanks = derived(groupRankTransformations, ($groupRankTransformations) => {
|
||||
const initialRanks: CommandGroupRanks = {
|
||||
const initialRanks = {
|
||||
...Object.fromEntries(groups.map((group) => [group, 0])),
|
||||
ungrouped: 9999,
|
||||
domains: 0,
|
||||
webhooks: 0,
|
||||
navigation: 1,
|
||||
projects: 0,
|
||||
account: 0,
|
||||
organizations: 0,
|
||||
auth: 0,
|
||||
platforms: 0,
|
||||
databases: 0,
|
||||
functions: 0,
|
||||
storage: 0,
|
||||
integrations: 0,
|
||||
migrations: 0,
|
||||
help: -1
|
||||
};
|
||||
} as CommandGroupRanks;
|
||||
|
||||
const transformations = Array.from($groupRankTransformations.values());
|
||||
return transformations.reduce((prev, curr) => curr(prev), initialRanks);
|
||||
});
|
||||
|
||||
export type Searcher = (query: string) => Promise<Command[]>;
|
||||
const searchersMap = writable<Map<string, Searcher>>(new Map());
|
||||
|
||||
export const registerSearcher = {
|
||||
subscribe(runner: (cb: (searcher: Searcher) => void) => void) {
|
||||
const uuid = crypto.randomUUID();
|
||||
|
||||
runner((searcher: Searcher) => {
|
||||
searchersMap.update((curr) => {
|
||||
curr.set(uuid, searcher);
|
||||
return curr;
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
searchersMap.update((curr) => {
|
||||
curr.delete(uuid);
|
||||
return curr;
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const searchers = derived(searchersMap, ($searchersMap) => {
|
||||
return Array.from($searchersMap.values());
|
||||
});
|
||||
|
||||
export const useSearcher = (searcher: Searcher) => {
|
||||
const search = writable('');
|
||||
const results = writable<Command[]>([]);
|
||||
|
||||
const searcherDebounced = debounce(async (query: string) => {
|
||||
results.set(await searcher(query));
|
||||
}, 500);
|
||||
|
||||
onMount(() => {
|
||||
searcherDebounced.immediate('');
|
||||
return search.subscribe((query) => {
|
||||
searcherDebounced(query);
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
search,
|
||||
results
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,58 +2,19 @@
|
||||
This is the root command panel. It precedes all other command panels.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { commands, type Command } from '../commands';
|
||||
import { isMac } from '$lib/helpers/platform';
|
||||
import Template from './template.svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Query } from '@appwrite.io/console';
|
||||
import { goto } from '$app/navigation';
|
||||
import { project } from '$routes/console/project-[project]/store';
|
||||
import { database } from '$routes/console/project-[project]/databases/database-[database]/store';
|
||||
import { debounce } from '$lib/helpers/debounce';
|
||||
import { isMac } from '$lib/helpers/platform';
|
||||
import { commands, searchers, type Command } from '../commands';
|
||||
import Template from './template.svelte';
|
||||
|
||||
let search = '';
|
||||
|
||||
let searchResults: Omit<Command, 'keys'>[] = [];
|
||||
const executeSearch = debounce(async (s: string) => {
|
||||
const newResults: typeof searchResults = [];
|
||||
const { databases } = await sdk.forProject.databases.list([Query.limit(20)]);
|
||||
newResults.push(
|
||||
...databases
|
||||
.filter((db) => db.name.includes(s))
|
||||
.map((db) => {
|
||||
return {
|
||||
label: `Go to ${db.name} database`,
|
||||
callback: () => {
|
||||
goto(`/console/project-${$project.$id}/databases/database-${db.$id}`);
|
||||
},
|
||||
group: 'databases'
|
||||
} as const;
|
||||
})
|
||||
);
|
||||
|
||||
if ($database?.$id) {
|
||||
const { collections } = await sdk.forProject.databases.listCollections($database.$id, [
|
||||
Query.limit(20)
|
||||
]);
|
||||
newResults.push(
|
||||
...collections
|
||||
.filter((collection) => collection.name.includes(s))
|
||||
.map((collection) => {
|
||||
return {
|
||||
label: `Go to ${collection.name} collection`,
|
||||
callback: () => {
|
||||
goto(
|
||||
`/console/project-${$project.$id}/databases/database-${$database.$id}/collection-${collection.$id}`
|
||||
);
|
||||
},
|
||||
group: 'databases'
|
||||
} as const;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
searchResults = [...newResults];
|
||||
$searchers.forEach(async (searcher) => {
|
||||
const results = await searcher(s);
|
||||
searchResults = [...searchResults, ...results];
|
||||
});
|
||||
}, 500);
|
||||
|
||||
$: {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { project } from '$routes/console/project-[project]/store';
|
||||
import { get } from 'svelte/store';
|
||||
import type { Searcher } from '../commands';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
|
||||
export const dbSearcher = (async (query: string) => {
|
||||
const { databases } = await sdk.forProject.databases.list();
|
||||
|
||||
return databases
|
||||
.filter((db) => db.name.toLowerCase().includes(query.toLowerCase()))
|
||||
.map(
|
||||
(db) =>
|
||||
({
|
||||
group: 'databases',
|
||||
label: db.name,
|
||||
callback: () => {
|
||||
goto(`/console/project-${get(project).$id}/databases/database-${db.$id}`);
|
||||
}
|
||||
} as const)
|
||||
);
|
||||
}) satisfies Searcher;
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './databases';
|
||||
export * from './users';
|
||||
@@ -0,0 +1,49 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { project } from '$routes/console/project-[project]/store';
|
||||
import { get } from 'svelte/store';
|
||||
import type { Command, Searcher } from '../commands';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
const getUserCommand = (user: Models.User<Models.Preferences>, projectId: string) =>
|
||||
({
|
||||
label: user.name,
|
||||
callback: () => {
|
||||
goto(`/console/project-${projectId}/auth/user-${user.$id}`);
|
||||
},
|
||||
group: 'users',
|
||||
icon: 'user-circle'
|
||||
} satisfies Command);
|
||||
|
||||
export const userSearcher = (async (query: string) => {
|
||||
const { users } = await sdk.forProject.users.list([], query);
|
||||
const projectId = get(project).$id;
|
||||
|
||||
if (users.length === 1) {
|
||||
return [
|
||||
getUserCommand(users[0], projectId),
|
||||
{
|
||||
label: 'Go to activity',
|
||||
callback: () => {
|
||||
goto(`/console/project-${projectId}/auth/user-${users[0].$id}/activity`);
|
||||
},
|
||||
group: 'users'
|
||||
},
|
||||
{
|
||||
label: 'Go to sessions',
|
||||
callback: () => {
|
||||
goto(`/console/project-${projectId}/auth/user-${users[0].$id}/sessions`);
|
||||
},
|
||||
group: 'users'
|
||||
},
|
||||
{
|
||||
label: 'Go to memberships',
|
||||
callback: () => {
|
||||
goto(`/console/project-${projectId}/auth/user-${users[0].$id}/memberships`);
|
||||
},
|
||||
group: 'users'
|
||||
}
|
||||
];
|
||||
}
|
||||
return users.map((user) => getUserCommand(user, projectId));
|
||||
}) satisfies Searcher;
|
||||
@@ -5,9 +5,10 @@
|
||||
import { project, stats } from './store';
|
||||
|
||||
import { goto } from '$app/navigation';
|
||||
import { registerCommands } from '$lib/commandCenter';
|
||||
import { registerCommands, registerSearcher } from '$lib/commandCenter';
|
||||
|
||||
import { MigrationBox } from '$lib/components';
|
||||
import { dbSearcher, userSearcher } from '$lib/commandCenter/searchers';
|
||||
|
||||
onMount(async () => {
|
||||
return sdk.forConsole.client.subscribe(['project', 'console'], (response) => {
|
||||
@@ -70,6 +71,9 @@
|
||||
group: 'navigation'
|
||||
}
|
||||
]);
|
||||
|
||||
$registerSearcher(dbSearcher);
|
||||
$registerSearcher(userSearcher);
|
||||
</script>
|
||||
|
||||
<slot />
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import {
|
||||
PUBLIC_MOCK_APIKEY,
|
||||
PUBLIC_MOCK_ENDPOINT,
|
||||
PUBLIC_MOCK_PROJECTID
|
||||
} from '$env/static/public';
|
||||
import { registerCommands, updateCommandGroupRanks } from '$lib/commandCenter';
|
||||
import { Arrow, CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
@@ -116,23 +111,23 @@
|
||||
undefined
|
||||
);
|
||||
|
||||
// const migrationData = {
|
||||
// endpoint: currEndpoint,
|
||||
// projectId: $project.$id,
|
||||
// apiKey: secret
|
||||
// };
|
||||
|
||||
const migrationData = {
|
||||
endpoint: PUBLIC_MOCK_ENDPOINT,
|
||||
projectId: PUBLIC_MOCK_PROJECTID,
|
||||
apiKey: PUBLIC_MOCK_APIKEY
|
||||
endpoint: currEndpoint,
|
||||
projectId: $project.$id,
|
||||
apiKey: secret
|
||||
};
|
||||
|
||||
window.location.href = `http://localhost:3000/?migrate=${encodeURIComponent(
|
||||
window.location.href = `https://cloud.appwrite.io/?migrate=${encodeURIComponent(
|
||||
JSON.stringify(migrationData)
|
||||
)}`;
|
||||
|
||||
// window.location.href = `https://cloud.appwrite.io/?migrate=${encodeURIComponent(
|
||||
// const migrationData = {
|
||||
// endpoint: PUBLIC_MOCK_ENDPOINT,
|
||||
// projectId: PUBLIC_MOCK_PROJECTID,
|
||||
// apiKey: PUBLIC_MOCK_APIKEY
|
||||
// };
|
||||
|
||||
// window.location.href = `http://localhost:3000/?migrate=${encodeURIComponent(
|
||||
// JSON.stringify(migrationData)
|
||||
// )}`;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user