update instance user name (#4294)

* feat(frontend): update instance user name

* feat(frontend): edit instance user name

* feat(frontend): improve UX

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
This commit is contained in:
Faton Ramadani
2024-08-29 12:41:53 +02:00
committed by GitHub
co-authored by Ruben Fiszel
parent 67494a068a
commit 773447e2e7
4 changed files with 239 additions and 138 deletions
@@ -1,75 +1,11 @@
<script lang="ts">
import { SettingService, UserService } from '$lib/gen'
import { Button, Popup } from './common'
import { sendUserToast } from '$lib/toast'
import Alert from './common/alert/Alert.svelte'
import { autoPlacement } from '@floating-ui/core'
import { createEventDispatcher } from 'svelte'
import ChangeInstanceUsernameInner from './ChangeInstanceUsernameInner.svelte'
export let email: string
export let username: string
export let isConflict = false
let loading = false
let usernameInfo:
| {
username: string
workspace_usernames: {
workspace_id: string
username: string
}[]
}
| undefined = undefined
function handleKeyUp(event: KeyboardEvent) {
const key = event.key
if (key === 'Enter') {
event.preventDefault()
renameUser()
}
}
async function getUsernameInfo() {
usernameInfo = await UserService.globalUsernameInfo({
email
})
if (isConflict) {
username = usernameInfo.username
}
}
getUsernameInfo()
const dispatch = createEventDispatcher()
async function renameUser() {
loading = true
try {
const automateUsernameCreation =
(await SettingService.getGlobal({ key: 'automate_username_creation' })) ?? false
if (!automateUsernameCreation) {
sendUserToast(
'Modifying the username is only possible when the creation of usernames is automated and defined at instance level..'
)
return
}
await UserService.globalUserRename({
email,
requestBody: {
new_username: username
}
})
sendUserToast(`Renamed user ${email} to ${username}`)
dispatch('renamed')
} finally {
loading = false
}
}
</script>
<Popup
@@ -88,67 +24,11 @@
>{isConflict ? 'Fix username conflict' : 'Change username'}</Button
>
</svelte:fragment>
<div class="flex flex-col max-w-2xl p-2">
<span class="text-sm mb-2 leading-6 font-semibold"
>{isConflict ? 'Fix username conflict' : 'Change username'}</span
>
<span class="text-xs mb-1 leading-6"
>{isConflict ? 'Auto-generated instance username' : 'New username'}</span
>
<input
type="text"
class="mb-4"
on:keyup={handleKeyUp}
bind:value={username}
disabled={isConflict}
/>
{#if isConflict}
<Alert title="Username conflict" class="mb-4">
Users are required to have an instance-wide username that is shared across all workspaces.
However, this user has different usernames in different workspaces.
{#if usernameInfo?.workspace_usernames && usernameInfo.workspace_usernames.filter((w) => w.username !== username).length > 0}
<br />
<br />
Workspaces requiring username modification: {usernameInfo.workspace_usernames
.filter((w) => w.username !== username)
.map((wu) => `${wu.workspace_id} (${wu.username})`)
.join(', ')}
{/if}
</Alert>
{/if}
{#if !isConflict && usernameInfo?.workspace_usernames && usernameInfo.workspace_usernames.filter((w) => w.username !== username).length > 0}
<Alert title="Concerned workspaces" class="mb-4">
{usernameInfo.workspace_usernames
.filter((w) => w.username !== username)
.map((wu) => `${wu.workspace_id}`)
.join(', ')}
</Alert>
{/if}
<Alert type="warning" title="Manual action required" class="mb-4">
This operation does not handle references in scripts, workflows and applications to scripts in
the workspace, and references in resources to variables. You will have to handle those
manually.
<br />
</Alert>
<Button
variant="contained"
color="blue"
size="xs"
on:click={() => {
renameUser().then(() => {
close(null)
})
}}
disabled={email === undefined || !username}
{loading}
>
Confirm username change
</Button>
</div>
<ChangeInstanceUsernameInner
{email}
{username}
{isConflict}
on:close={() => close(null)}
on:renamed
/>
</Popup>
@@ -0,0 +1,135 @@
<script lang="ts">
import { SettingService, UserService } from '$lib/gen'
import { Button } from './common'
import { sendUserToast } from '$lib/toast'
import Alert from './common/alert/Alert.svelte'
import { createEventDispatcher } from 'svelte'
export let email: string
export let username: string
export let isConflict = false
let loading = false
let usernameInfo:
| {
username: string
workspace_usernames: {
workspace_id: string
username: string
}[]
}
| undefined = undefined
function handleKeyUp(event: KeyboardEvent) {
const key = event.key
if (key === 'Enter') {
event.preventDefault()
renameUser()
}
}
async function getUsernameInfo() {
usernameInfo = await UserService.globalUsernameInfo({
email
})
if (isConflict) {
username = usernameInfo.username
}
}
getUsernameInfo()
const dispatch = createEventDispatcher()
async function renameUser() {
loading = true
try {
const automateUsernameCreation =
(await SettingService.getGlobal({ key: 'automate_username_creation' })) ?? false
if (!automateUsernameCreation) {
sendUserToast(
'Modifying the username is only possible when the creation of usernames is automated and defined at instance level..'
)
return
}
await UserService.globalUserRename({
email,
requestBody: {
new_username: username
}
})
sendUserToast(`Renamed user ${email} to ${username}`)
dispatch('renamed')
} finally {
loading = false
}
}
</script>
<div class="flex flex-col max-w-2xl p-2">
<span class="text-sm mb-2 leading-6 font-semibold"
>{isConflict ? 'Fix username conflict' : 'Change username'}</span
>
<span class="text-xs mb-1 leading-6"
>{isConflict ? 'Auto-generated instance username' : 'New username'}</span
>
<input
type="text"
class="mb-4"
on:keyup={handleKeyUp}
bind:value={username}
disabled={isConflict}
/>
{#if isConflict}
<Alert title="Username conflict" class="mb-4">
Users are required to have an instance-wide username that is shared across all workspaces.
However, this user has different usernames in different workspaces.
{#if usernameInfo?.workspace_usernames && usernameInfo.workspace_usernames.filter((w) => w.username !== username).length > 0}
<br />
<br />
Workspaces requiring username modification: {usernameInfo.workspace_usernames
.filter((w) => w.username !== username)
.map((wu) => `${wu.workspace_id} (${wu.username})`)
.join(', ')}
{/if}
</Alert>
{/if}
{#if !isConflict && usernameInfo?.workspace_usernames && usernameInfo.workspace_usernames.filter((w) => w.username !== username).length > 0}
<Alert title="Concerned workspaces" class="mb-4">
{usernameInfo.workspace_usernames
.filter((w) => w.username !== username)
.map((wu) => `${wu.workspace_id}`)
.join(', ')}
</Alert>
{/if}
<Alert type="warning" title="Manual action required" class="mb-4">
This operation does not handle references in scripts, workflows and applications to scripts in
the workspace, and references in resources to variables. You will have to handle those manually.
<br />
</Alert>
<Button
variant="contained"
color="blue"
size="xs"
on:click={() => {
renameUser().then(() => {
dispatch('close')
})
}}
disabled={email === undefined || !username}
{loading}
>
Confirm username change
</Button>
</div>
@@ -0,0 +1,68 @@
<script lang="ts">
import { Settings } from 'lucide-svelte'
import { createEventDispatcher } from 'svelte'
import Button from './common/button/Button.svelte'
import Popup from './common/popup/Popup.svelte'
import { offset, flip, shift } from 'svelte-floating-ui/dom'
import ChangeInstanceUsernameInner from './ChangeInstanceUsernameInner.svelte'
export let value: string | undefined
export let email: string
export let username: string | undefined = undefined
export let automateUsernameCreation: boolean = false
const dispatch = createEventDispatcher()
function save() {
dispatch('save', value)
}
</script>
<Popup
let:close
floatingConfig={{
strategy: 'fixed',
placement: 'left-end',
middleware: [offset(8), flip(), shift()]
}}
>
<svelte:fragment slot="button">
<Button nonCaptureEvent={true} size="xs" color="light" startIcon={{ icon: Settings }} />
</svelte:fragment>
<div class="flex flex-col gap-8 max-w-sm">
{#if automateUsernameCreation && username}
<ChangeInstanceUsernameInner {email} {username} on:renamed />
{/if}
<label class="block text-primary">
<div class="pb-1 text-xs text-secondary">Name</div>
<div class="flex w-full">
<input
type="text"
bind:value
class="!w-auto grow"
on:click|stopPropagation={() => {}}
on:keydown|stopPropagation
on:keypress|stopPropagation={({ key }) => {
if (key === 'Enter') {
save()
close(null)
}
}}
/>
</div>
<Button
size="xs"
color="blue"
buttonType="button"
btnClasses="mt-2 "
aria-label="Save ID"
on:click={() => {
save()
close(null)
}}
>
Update name
</Button>
</label>
</div>
</Popup>
@@ -22,6 +22,7 @@
import ChangeInstanceUsername from './ChangeInstanceUsername.svelte'
import Tooltip from './Tooltip.svelte'
import { isCloudHosted } from '$lib/cloud'
import InstanceNameEditor from './InstanceNameEditor.svelte'
let drawer: Drawer
let filter = ''
@@ -72,6 +73,21 @@
sendUserToast('Automatic username creation enabled')
listUsers()
}
async function updateName(name: string | undefined, email: string) {
try {
await UserService.globalUserUpdate({
email,
requestBody: {
name
}
})
sendUserToast('User updated')
listUsers()
} catch (e) {
sendUserToast('Error updating user', true)
}
}
</script>
<SearchItems
@@ -222,15 +238,18 @@
</td>
<td>
<div class="flex flex-row gap-x-1 justify-end">
{#if automateUsernameCreation && username}
<ChangeInstanceUsername
{username}
{email}
on:renamed={() => {
listUsers()
}}
/>
{/if}
<InstanceNameEditor
value={name}
{username}
{email}
on:save={(e) => {
updateName(e.detail, email)
}}
on:renamed={() => {
listUsers()
}}
{automateUsernameCreation}
/>
<Button
color="light"
variant="contained"
@@ -239,7 +258,6 @@
btnClasses="text-red-500"
on:click={() => {
deleteConfirmedCallback = async () => {
console.log(email)
await UserService.globalUserDelete({ email })
sendUserToast(`User ${email} removed`)
listUsers()