Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 33 additions & 35 deletions ui/user/src/lib/components/admin/AccessControlRuleForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import type { PoweruserWorkspaceContext } from '$lib/context/poweruserWorkspace.svelte';
import { getRegistryLabel, getUserDisplayName } from '$lib/utils';
import { profile } from '$lib/stores';
import { KV, KVSync } from '$lib/kv';

interface Props {
topContent?: Snippet;
Expand All @@ -40,6 +41,9 @@
readonly?: boolean;
}

const kv = KV.get();
const kvSync = new KVSync(kv!);

let {
topContent,
accessControlRule: initialAccessControlRule,
Expand Down Expand Up @@ -93,41 +97,7 @@
return;
}

loadingUsersAndGroups = true;

// Prevent refetching when adding new users or groups
const promises: [Promise<OrgUser[] | undefined>, Promise<OrgGroup[] | undefined>] = [
Promise.resolve(undefined),
Promise.resolve(undefined)
];

if (!usersAndGroups?.users) {
promises[0] = AdminService.listUsers();
}
if (!usersAndGroups?.groups) {
promises[1] = AdminService.listGroups();
}

Promise.all(promises)
.then(([users, groups]) => {
if (!usersAndGroups) {
usersAndGroups = { users: [], groups: [] };
}

if (users) {
usersAndGroups!.users = users;
}

if (groups) {
usersAndGroups!.groups = groups;
}

loadingUsersAndGroups = false;
})
.catch((error) => {
console.error('Failed to load users and groups:', error);
loadingUsersAndGroups = false;
});
loadData();
});

$effect(() => {
Expand Down Expand Up @@ -173,6 +143,34 @@
}
});

async function loadData() {
try {
loadingUsersAndGroups = true;

// Prevent refetching when adding new users or groups
const promises: [Promise<OrgUser[] | undefined>, Promise<OrgGroup[] | undefined>] = [
Promise.resolve(undefined),
Promise.resolve(undefined)
];

promises[0] = kvSync!.get('users', () => AdminService.listUsers(), 1000 * 60 * 5);
promises[1] = kvSync.get('groups', () => AdminService.listGroups(), 1000 * 60 * 5);

const [users, groups] = await Promise.all(promises);

if (!usersAndGroups) {
usersAndGroups = { users: [], groups: [] };
}

usersAndGroups!.users = users ?? [];
usersAndGroups!.groups = groups ?? [];

loadingUsersAndGroups = false;
} catch (error) {
console.error('Error initializing data:', error);
}
}

function convertSubjectsToTableData(
subjects: AccessControlRuleSubject[],
users: OrgUser[],
Expand Down
31 changes: 26 additions & 5 deletions ui/user/src/lib/components/admin/SearchMcpServers.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { ADMIN_ALL_OPTION } from '$lib/constants';
import { getUserDisplayName } from '$lib/utils';
import { AdminService, type OrgUser } from '$lib/services';
import { onMount } from 'svelte';
import { KV, KVSync } from '$lib/kv';

interface Props {
onAdd: (mcpCatalogEntryIds: string[], mcpServerIds: string[], otherSelectors: string[]) => void;
Expand All @@ -28,13 +28,16 @@
registry?: string;
};

const kv = KV.get();
const kvSync = new KVSync(kv!);

let { onAdd, exclude, mcpEntriesContextFn, all = ADMIN_ALL_OPTION, type }: Props = $props();
let addMcpServerDialog = $state<ReturnType<typeof ResponsiveDialog>>();
let users = $state<OrgUser[]>([]);
let search = $state('');
let selected = $state<SearchItem[]>([]);
let selectedMap = $derived(new Set(selected.map((i) => i.id)));
let usersMap = $derived(new Map(users.map((user) => [user.id, user])));
let usersMap = $derived(new Map(users?.map((user) => [user.id, user])));

const mcpServerAndEntries = mcpEntriesContextFn?.() ?? {
entries: [],
Expand Down Expand Up @@ -81,6 +84,10 @@
: allData
);

$effect.pre(() => {
loadData();
});

export function open() {
addMcpServerDialog?.open();
}
Expand All @@ -107,9 +114,23 @@
addMcpServerDialog?.close();
}

onMount(async () => {
users = await AdminService.listUsersIncludeDeleted();
});
async function loadData() {
try {
loading = true;

const u = await kvSync!.get(
'users',
() => AdminService.listUsersIncludeDeleted(),
1000 * 60 * 5
);

users = u ?? [];

loading = false;
} catch (error) {
console.error('Error initializing data:', error);
}
}
</script>

<ResponsiveDialog
Expand Down
55 changes: 36 additions & 19 deletions ui/user/src/lib/components/admin/SearchUsers.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@
import Search from '../Search.svelte';
import ResponsiveDialog from '../ResponsiveDialog.svelte';
import { getUserRoleLabel } from '$lib/utils';
import { KV, KVSync } from '$lib/kv';

interface Props {
onAdd: (users: OrgUser[], groups: OrgGroup[]) => void;
filterIds?: string[];
}

const kv = KV.get();
const kvSync = new KVSync(kv!);

let { onAdd, filterIds }: Props = $props();

let addUserGroupDialog = $state<ReturnType<typeof ResponsiveDialog>>();
let users = $state<OrgUser[]>([]);
let groups = $state<OrgGroup[]>([]);
let loading = $state(false);
let searchNames = $state('');
let selectedUsers = $state<(OrgUser | OrgGroup)[]>([]);
Expand Down Expand Up @@ -48,16 +53,12 @@
)
: users;

try {
// Fetch groups with server-side search
filteredGroups = (
await AdminService.listGroups(searchNames.length > 0 ? { query: searchNames } : undefined)
).sort((a, b) => a.name.localeCompare(b.name));
} catch (error) {
console.error('Error loading groups:', error);
} finally {
loading = false;
}
filteredGroups =
searchNames.length > 0
? groups.filter((group) => group.name.toLowerCase().includes(searchNames.toLowerCase()))
: groups;

loading = false;
}

const handleSearch = debounce(() => {
Expand All @@ -70,15 +71,7 @@
}

async function onOpen() {
loading = true;

try {
users = await AdminService.listUsers();
} catch (error) {
console.error('Error loading initial users:', error);
} finally {
loading = false;
}
await loadData();

// Now search to populate filtered data
await search();
Expand All @@ -91,6 +84,30 @@
filteredUsers = [];
filteredGroups = [];
}

async function loadData() {
try {
loading = true;

// Prevent refetching when adding new users or groups
const promises: [Promise<OrgUser[] | undefined>, Promise<OrgGroup[] | undefined>] = [
Promise.resolve(undefined),
Promise.resolve(undefined)
];

promises[0] = kvSync!.get('users', () => AdminService.listUsers(), 1000 * 60 * 5);
promises[1] = kvSync.get('groups', () => AdminService.listGroups(), 1000 * 60 * 5);

const [u, g] = await Promise.all(promises);

users = u ?? [];
groups = g ?? [];

loading = false;
} catch (error) {
console.error('Error initializing data:', error);
}
}
</script>

<ResponsiveDialog
Expand Down
1 change: 1 addition & 0 deletions ui/user/src/lib/kv/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { KV, KVSync } from './kv';
Loading