diff --git a/frontend/src/lib/components/AddUser.svelte b/frontend/src/lib/components/AddUser.svelte index 6c3d56c98a..1872684410 100644 --- a/frontend/src/lib/components/AddUser.svelte +++ b/frontend/src/lib/components/AddUser.svelte @@ -91,7 +91,7 @@ {/if} - Role + Role {#snippet children({ item })} - import { sendUserToast } from '$lib/toast' - import { createEventDispatcher } from 'svelte' - import { globalEmailInvite, superadmin, workspaceStore } from '$lib/stores' - import { UserService, WorkspaceService } from '$lib/gen' import { Button } from './common' import Popover from './meltComponents/Popover.svelte' - import { isCloudHosted } from '$lib/cloud' - import { goto } from '$lib/navigation' + import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte' import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte' import { MailPlus } from 'lucide-svelte' - const dispatch = createEventDispatcher() + interface Props { + inviteUser: (email: string, selected: 'operator' | 'developer' | 'admin') => Promise + } + + const { inviteUser }: Props = $props() let email: string = $state('') @@ -19,55 +18,22 @@ const key = event.key if (key === 'Enter') { event.preventDefault() - inviteUser() + inviteUser(email, selected) + email = '' } } - async function inviteUser() { - await WorkspaceService.inviteUser({ - workspace: $workspaceStore!, - requestBody: { - email, - is_admin: selected == 'admin', - operator: selected == 'operator' - } - }) - sendUserToast(`Invited ${email}`) - if (!(await UserService.existsEmail({ email }))) { - let isSuperadmin = $superadmin - if (!isCloudHosted()) { - sendUserToast( - `User ${email} is not registered yet on the instance. ${ - !isSuperadmin - ? `If not using SSO, ask an administrator to add ${email} to the instance` - : '' - }`, - true, - isSuperadmin - ? [ - { - label: 'Add user to the instance', - callback: () => { - $globalEmailInvite = email - goto('#superadmin-settings') - } - } - ] - : [] - ) - } - } - dispatch('new') - - email = '' - } - let selected: 'operator' | 'developer' | 'admin' = $state('developer') {#snippet trigger()} - {/snippet} @@ -104,7 +70,12 @@ /> {/snippet} - diff --git a/frontend/src/lib/components/Section.svelte b/frontend/src/lib/components/Section.svelte index ee3f794130..17610c1598 100644 --- a/frontend/src/lib/components/Section.svelte +++ b/frontend/src/lib/components/Section.svelte @@ -88,7 +88,9 @@ {/if} {/if} - {@render action?.()} + {#if !(collapsable && collapsed)} + {@render action?.()} + {/if} {#if collapsable && collapsed} {@render badge?.()} {/if} diff --git a/frontend/src/lib/components/settings/WorkspaceOperatorSettings.svelte b/frontend/src/lib/components/settings/WorkspaceOperatorSettings.svelte index 24cca0fc58..6aaa69cc53 100644 --- a/frontend/src/lib/components/settings/WorkspaceOperatorSettings.svelte +++ b/frontend/src/lib/components/settings/WorkspaceOperatorSettings.svelte @@ -88,89 +88,72 @@ ) -
-
-
-
-
- Configure the operator visibility settings for your workspace. Toggle the settings you - want to enable. -
-
-
+
+ {#snippet action()} + + {/snippet} -
-
-
- -
- - - - Section - Description - - (allDisabled ? 'false' : allEnabled ? 'true' : ''), - (v) => { - Object.keys(operatorWorkspaceSettings).forEach((key) => { - if (v === 'true') operatorWorkspaceSettings[key] = true - if (v === 'false') operatorWorkspaceSettings[key] = false - }) - } - } - > - {#snippet children({ item })} - - - {/snippet} - - - - - - {#each Object.entries(descriptions) as [key, { title, description }]} - - {title} - {description} - - (operatorWorkspaceSettings[key] = detail === 'on')} - > - {#snippet children({ item })} - - - {/snippet} - - - - {/each} - - -
- -
- -
-
-
+ + + + Section + Description + + (allDisabled ? 'false' : allEnabled ? 'true' : ''), + (v) => { + Object.keys(operatorWorkspaceSettings).forEach((key) => { + if (v === 'true') operatorWorkspaceSettings[key] = true + if (v === 'false') operatorWorkspaceSettings[key] = false + }) + } + } + > + {#snippet children({ item })} + + + {/snippet} + + + + + + {#each Object.entries(descriptions) as [key, { title, description }]} + + {title} + {description} + + (operatorWorkspaceSettings[key] = detail === 'on')} + > + {#snippet children({ item })} + + + {/snippet} + + + + {/each} + + + diff --git a/frontend/src/lib/components/settings/WorkspaceUserSettings.svelte b/frontend/src/lib/components/settings/WorkspaceUserSettings.svelte index 6d36e31d4b..1b9d229bbb 100644 --- a/frontend/src/lib/components/settings/WorkspaceUserSettings.svelte +++ b/frontend/src/lib/components/settings/WorkspaceUserSettings.svelte @@ -1,12 +1,11 @@ -
- -
- - +
+ {#snippet action()} +
+ + - - {#snippet trigger()} - - {/snippet} - {#snippet content()} -
- - {isCloudHosted() - ? `Auto-add anyone from ${ - auto_invite_domain != undefined ? auto_invite_domain : domain - }` - : `Auto-add anyone joining the instance`} - - - {#if showInvites} - + {#snippet trigger()} + + {/snippet} + {#snippet content()} +
+
Mode Whether to invite or add users directly to the workspace. - +
{ - if (auto_invite_domain != undefined) { - await removeAllInvitesFromDomain() - await WorkspaceService.editAutoInvite({ - workspace: $workspaceStore ?? '', - requestBody: { - operator: operatorOnly ?? false, - invite_all: !isCloudHosted(), - auto_add: e.detail === 'add' - } - }) - loadSettings() - listInvites() - listUsers() - autoAdd = e.detail === 'add' - } else { - autoAdd = e.detail === 'add' + autoAdd = e.detail === 'add' + if (autoInviteOrAddEnabled) { + await updateAutoInvite(true) } }} > @@ -375,264 +426,239 @@ {/snippet} - {/if} - Role Role of the auto-added users - { - if (auto_invite_domain != undefined) { - await removeAllInvitesFromDomain() - await WorkspaceService.editAutoInvite({ - workspace: $workspaceStore ?? '', - requestBody: { - operator: e.detail === 'operator', - invite_all: !isCloudHosted(), - auto_add: showInvites ? (autoAdd ?? false) : true - } - }) + Role Role of the auto-added users + { operatorOnly = e.detail === 'operator' - loadSettings() - listInvites() - listUsers() - } else { - operatorOnly = e.detail === 'operator' - } - }} - > - {#snippet children({ item })} - - - {/snippet} - -
- { - await removeAllInvitesFromDomain() - await WorkspaceService.editAutoInvite({ - workspace: $workspaceStore ?? '', - requestBody: e.detail - ? { - operator: operatorOnly ?? false, - invite_all: !isCloudHosted(), - auto_add: showInvites ? (autoAdd ?? false) : true - } - : { - operator: undefined, - auto_add: undefined - } - }) - loadSettings() - listInvites() - listUsers() + if (auto_invite_domain != undefined) { + await updateAutoInvite(true) + } }} - disabled={isCloudHosted() && !allowedAutoDomain} - options={{ - right: 'Enabled' - }} - /> -
- {#if isCloudHosted() && !allowedAutoDomain} -
{domain} domain not allowed for auto-add
- {/if} -
- {/snippet} - + > + {#snippet children({ item })} + + + {/snippet} + - {#if instanceGroups.length > 0} - - {#snippet trigger()} - - {/snippet} - {#snippet content()} -
-
- Auto-add instance groups - - - {#if availableGroupItems.length > 0} -
-
-
- Instance group - +
+ +
+ Role + { + selectedNewRole = e.detail + }} + > + {#snippet children({ item })} + + + + {/snippet} + +
+
+ + +
+ {/if} + + + {#if autoAddInstanceGroups.length > 0} +
+

Configured groups:

+
+ + + + + + + + + + {#each autoAddInstanceGroups as groupName (groupName)} + {@const group = instanceGroups.find((g) => g.name === groupName)} + + + + + + {/each} + +
GroupRole
+
{groupName}
+ {#if group?.summary} +
{group.summary}
+ {/if} +
+
+ { + autoAddInstanceGroupsRoles[groupName] = e.detail + await updateGroupRole(groupName, e.detail) + }} + > + {#snippet children({ item })} + + + + {/snippet} + +
+
+
+ +
+
+
+
+ {:else} +
+ No instance groups configured for auto-add +
+ {/if} +
+
+ {/snippet} +
+ {/if} + + + + { + listUsers() + listInvites() + }} + /> +
+ {/snippet} -
30} loadMore={30} @@ -661,8 +687,8 @@ first one - Status Role + Enabled Actions @@ -703,15 +729,6 @@ class="animate-spin" />{/if} - -
- {#if disabled} - Disabled - {:else} - Enabled - {/if} -
-
{#if added_via?.source === 'instance_group'} @@ -775,13 +792,10 @@
-
- + } catch (e) { + console.error('Failed to update user status:', e) + sendUserToast('Failed to update user status', true) + } + }} + size="xs" + /> + + +
+ {#snippet removeUserButton(disabled: boolean)} + + {/snippet} {#if added_via?.source === 'instance_group'}
- + {@render removeUserButton(true)} Cannot remove users synced from instance groups. Either disable the user or remove them from the SCIM group. {:else if canConvertToGroup(user)} - {:else} - + {@render removeUserButton(false)} {/if}
@@ -882,23 +875,25 @@ {/if} -
+
+ +
-{#if showInvites} -
+ +{#if invites?.length > 0} +
-
- -
- - -
+ {#snippet action()} +
+ +
+ {/snippet} @@ -959,9 +954,13 @@
- + Cancel invite + {/each} @@ -993,9 +992,8 @@ {/if} -
+ {/if} - +
{#if subItem.icon} @@ -682,7 +687,7 @@ { leaveWorkspaceModal = false }} diff --git a/frontend/src/lib/components/workspaceSettings/CreateWorkspace.svelte b/frontend/src/lib/components/workspaceSettings/CreateWorkspace.svelte index 6f6041cc4b..232a6cf262 100644 --- a/frontend/src/lib/components/workspaceSettings/CreateWorkspace.svelte +++ b/frontend/src/lib/components/workspaceSettings/CreateWorkspace.svelte @@ -213,7 +213,7 @@ if (auto_invite) { await WorkspaceService.editAutoInvite({ workspace: id, - requestBody: { operator: operatorOnly, invite_all: !isCloudHosted(), auto_add: true } + requestBody: { operator: operatorOnly, invite_all: !isCloudHosted(), auto_add: autoAdd } }) } if (aiKey != '') { @@ -327,6 +327,7 @@ let auto_invite = $state(false) let operatorOnly = $state(false) + let autoAdd = $state(false) let selected: Exclude = $state('openai') run(() => { id = name.toLowerCase().replace(/\s/gi, '-') @@ -560,6 +561,17 @@ Whether to invite or add users directly to the workspace. + { + autoAdd = e.detail === 'add' + }} + > + {#snippet children({ item })} + + + {/snippet} +