Fix multiple issues in user details page
This commit is contained in:
parent
ea6356147b
commit
f5f510a482
|
@ -16,7 +16,7 @@
|
||||||
return list.map(item => {
|
return list.map(item => {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
selected: selected.find(x => x._id === item._id) != null,
|
selected: selected.find(x => x === item._id) != null,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,51 +48,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const adduserToGroup = async id => {
|
|
||||||
const user = await users.get(id)
|
|
||||||
if (!user?._id) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check we haven't already been added
|
|
||||||
if (group.users?.find(x => x._id === user._id)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update group
|
|
||||||
await groups.actions.save({
|
|
||||||
...group,
|
|
||||||
users: [...group.users, { _id: user._id, email: user.email }],
|
|
||||||
})
|
|
||||||
|
|
||||||
// Update user
|
|
||||||
let userGroups = user.userGroups || []
|
|
||||||
userGroups.push(groupId)
|
|
||||||
await users.save({
|
|
||||||
...user,
|
|
||||||
userGroups,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const removeUserFromGroup = async id => {
|
|
||||||
const user = await users.get(id)
|
|
||||||
if (!user?._id) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update group
|
|
||||||
await groups.actions.save({
|
|
||||||
...group,
|
|
||||||
users: group.users.filter(x => x._id !== id),
|
|
||||||
})
|
|
||||||
|
|
||||||
// Update user
|
|
||||||
await users.save({
|
|
||||||
...user,
|
|
||||||
userGroups: user.userGroups.filter(x => x !== groupId),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchUsers(page, search) {
|
async function fetchUsers(page, search) {
|
||||||
if ($pageInfo.loading) {
|
if ($pageInfo.loading) {
|
||||||
return
|
return
|
||||||
|
@ -191,10 +146,10 @@
|
||||||
<UserGroupPicker
|
<UserGroupPicker
|
||||||
bind:searchTerm
|
bind:searchTerm
|
||||||
labelKey="email"
|
labelKey="email"
|
||||||
selected={group.users}
|
selected={group.users?.map(user => user._id)}
|
||||||
list={$users.data}
|
list={$users.data}
|
||||||
on:select={e => adduserToGroup(e.detail)}
|
on:select={e => groups.actions.addUser(groupId, e.detail)}
|
||||||
on:deselect={e => removeUserFromGroup(e.detail)}
|
on:deselect={e => groups.actions.removeUser(groupId, e.detail)}
|
||||||
/>
|
/>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
|
@ -209,7 +164,7 @@
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
on:click={e => {
|
on:click={e => {
|
||||||
removeUserFromGroup(user._id)
|
groups.actions.removeUser(groupId, user._id)
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
}}
|
}}
|
||||||
hoverable
|
hoverable
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
StatusLight,
|
StatusLight,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { fetchData } from "helpers"
|
|
||||||
import { users, auth, groups, apps } from "stores/portal"
|
import { users, auth, groups, apps } from "stores/portal"
|
||||||
import { roles } from "stores/backend"
|
import { roles } from "stores/backend"
|
||||||
import { Constants } from "@budibase/frontend-core"
|
import { Constants } from "@budibase/frontend-core"
|
||||||
|
@ -38,28 +37,23 @@
|
||||||
let popoverAnchor
|
let popoverAnchor
|
||||||
let searchTerm = ""
|
let searchTerm = ""
|
||||||
let popover
|
let popover
|
||||||
let selectedGroups = []
|
|
||||||
let allAppList = []
|
let allAppList = []
|
||||||
let user
|
let user
|
||||||
let loaded = false
|
let loaded = false
|
||||||
|
|
||||||
$: fetchUser(userId)
|
$: fullName = user?.firstName ? user?.firstName + " " + user?.lastName : ""
|
||||||
$: fullName = $userFetch?.data?.firstName
|
$: nameLabel = getNameLabel(user)
|
||||||
? $userFetch?.data?.firstName + " " + $userFetch?.data?.lastName
|
|
||||||
: ""
|
|
||||||
$: nameLabel = getNameLabel($userFetch)
|
|
||||||
$: initials = getInitials(nameLabel)
|
$: initials = getInitials(nameLabel)
|
||||||
|
$: filteredGroups = getFilteredGroups($groups, searchTerm)
|
||||||
$: allAppList = $apps
|
$: allAppList = $apps
|
||||||
.filter(x => {
|
.filter(x => {
|
||||||
if ($userFetch.data?.roles) {
|
return Object.keys(user?.roles || {}).find(y => {
|
||||||
return Object.keys($userFetch.data.roles).find(y => {
|
return x.appId === apps.extractAppId(y)
|
||||||
return x.appId === apps.extractAppId(y)
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.map(app => {
|
.map(app => {
|
||||||
let roles = Object.fromEntries(
|
let roles = Object.fromEntries(
|
||||||
Object.entries($userFetch.data.roles).filter(([key]) => {
|
Object.entries(user?.roles).filter(([key]) => {
|
||||||
return apps.extractAppId(key) === app.appId
|
return apps.extractAppId(key) === app.appId
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -70,27 +64,27 @@
|
||||||
roles,
|
roles,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// Used for searching through groups in the add group popover
|
|
||||||
$: filteredGroups = $groups.filter(
|
|
||||||
group =>
|
|
||||||
selectedGroups &&
|
|
||||||
group?.name?.toLowerCase().includes(searchTerm.toLowerCase())
|
|
||||||
)
|
|
||||||
$: userGroups = $groups.filter(x => {
|
$: userGroups = $groups.filter(x => {
|
||||||
return x.users?.find(y => {
|
return x.users?.find(y => {
|
||||||
return y._id === userId
|
return y._id === userId
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
$: globalRole = $userFetch?.data?.admin?.global
|
$: globalRole = user?.admin?.global
|
||||||
? "admin"
|
? "admin"
|
||||||
: $userFetch?.data?.builder?.global
|
: user?.builder?.global
|
||||||
? "developer"
|
? "developer"
|
||||||
: "appUser"
|
: "appUser"
|
||||||
|
|
||||||
const userFetch = fetchData(`/api/global/users/${userId}`)
|
const getFilteredGroups = (groups, search) => {
|
||||||
|
if (!search) {
|
||||||
|
return groups
|
||||||
|
}
|
||||||
|
search = search.toLowerCase()
|
||||||
|
return groups.filter(group => group.name?.toLowerCase().includes(search))
|
||||||
|
}
|
||||||
|
|
||||||
const getNameLabel = userFetch => {
|
const getNameLabel = user => {
|
||||||
const { firstName, lastName, email } = userFetch?.data || {}
|
const { firstName, lastName, email } = user || {}
|
||||||
if (!firstName && !lastName) {
|
if (!firstName && !lastName) {
|
||||||
return email || ""
|
return email || ""
|
||||||
}
|
}
|
||||||
|
@ -136,24 +130,17 @@
|
||||||
}
|
}
|
||||||
async function updateUserFirstName(evt) {
|
async function updateUserFirstName(evt) {
|
||||||
try {
|
try {
|
||||||
await users.save({ ...$userFetch?.data, firstName: evt.target.value })
|
await users.save({ ...user, firstName: evt.target.value })
|
||||||
await userFetch.refresh()
|
await fetchUser()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error("Error updating user")
|
notifications.error("Error updating user")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeGroup(id) {
|
|
||||||
let updatedGroup = $groups.find(x => x._id === id)
|
|
||||||
let newUsers = updatedGroup.users.filter(user => user._id !== userId)
|
|
||||||
updatedGroup.users = newUsers
|
|
||||||
groups.actions.save(updatedGroup)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateUserLastName(evt) {
|
async function updateUserLastName(evt) {
|
||||||
try {
|
try {
|
||||||
await users.save({ ...$userFetch?.data, lastName: evt.target.value })
|
await users.save({ ...user, lastName: evt.target.value })
|
||||||
await userFetch.refresh()
|
await fetchUser()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error("Error updating user")
|
notifications.error("Error updating user")
|
||||||
}
|
}
|
||||||
|
@ -169,25 +156,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addGroup(groupId) {
|
async function fetchUser() {
|
||||||
let selectedGroup = selectedGroups.includes(groupId)
|
user = await users.get(userId)
|
||||||
let group = $groups.find(group => group._id === groupId)
|
|
||||||
|
|
||||||
if (selectedGroup) {
|
|
||||||
selectedGroups = selectedGroups.filter(id => id === selectedGroup)
|
|
||||||
let newUsers = group.users.filter(groupUser => user._id !== groupUser._id)
|
|
||||||
group.users = newUsers
|
|
||||||
} else {
|
|
||||||
selectedGroups = [...selectedGroups, groupId]
|
|
||||||
group.users.push(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
await groups.actions.save(group)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchUser(userId) {
|
|
||||||
let userPromise = users.get(userId)
|
|
||||||
user = await userPromise
|
|
||||||
if (!user?._id) {
|
if (!user?._id) {
|
||||||
$goto("./")
|
$goto("./")
|
||||||
}
|
}
|
||||||
|
@ -195,17 +165,31 @@
|
||||||
|
|
||||||
async function toggleFlags(detail) {
|
async function toggleFlags(detail) {
|
||||||
try {
|
try {
|
||||||
await users.save({ ...$userFetch?.data, ...detail })
|
await users.save({ ...user, ...detail })
|
||||||
await userFetch.refresh()
|
await fetchUser()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error("Error updating user")
|
notifications.error("Error updating user")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addAll() {}
|
const addGroup = async groupId => {
|
||||||
|
await groups.actions.addUser(groupId, userId)
|
||||||
|
await fetchUser()
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeGroup = async groupId => {
|
||||||
|
await groups.actions.removeUser(groupId, userId)
|
||||||
|
await fetchUser()
|
||||||
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
try {
|
try {
|
||||||
await Promise.all([groups.actions.init(), apps.load(), roles.fetch()])
|
await Promise.all([
|
||||||
|
fetchUser(),
|
||||||
|
groups.actions.init(),
|
||||||
|
apps.load(),
|
||||||
|
roles.fetch(),
|
||||||
|
])
|
||||||
loaded = true
|
loaded = true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error("Error getting user groups")
|
notifications.error("Error getting user groups")
|
||||||
|
@ -228,8 +212,8 @@
|
||||||
<Avatar size="XXL" {initials} />
|
<Avatar size="XXL" {initials} />
|
||||||
<div class="subtitle">
|
<div class="subtitle">
|
||||||
<Heading size="S">{nameLabel}</Heading>
|
<Heading size="S">{nameLabel}</Heading>
|
||||||
{#if nameLabel !== $userFetch?.data?.email}
|
{#if nameLabel !== user?.email}
|
||||||
<Body size="S">{$userFetch?.data?.email}</Body>
|
<Body size="S">{user?.email}</Body>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -256,21 +240,15 @@
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<Label size="L">Email</Label>
|
<Label size="L">Email</Label>
|
||||||
<Input disabled value={$userFetch?.data?.email} />
|
<Input disabled value={user?.email} />
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<Label size="L">First name</Label>
|
<Label size="L">First name</Label>
|
||||||
<Input
|
<Input value={user?.firstName} on:blur={updateUserFirstName} />
|
||||||
value={$userFetch?.data?.firstName}
|
|
||||||
on:blur={updateUserFirstName}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<Label size="L">Last name</Label>
|
<Label size="L">Last name</Label>
|
||||||
<Input
|
<Input value={user?.lastName} on:blur={updateUserLastName} />
|
||||||
value={$userFetch?.data?.lastName}
|
|
||||||
on:blur={updateUserLastName}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- don't let a user remove the privileges that let them be here -->
|
<!-- don't let a user remove the privileges that let them be here -->
|
||||||
{#if userId !== $auth.user._id}
|
{#if userId !== $auth.user._id}
|
||||||
|
@ -304,13 +282,12 @@
|
||||||
</div>
|
</div>
|
||||||
<Popover align="right" bind:this={popover} anchor={popoverAnchor}>
|
<Popover align="right" bind:this={popover} anchor={popoverAnchor}>
|
||||||
<UserGroupPicker
|
<UserGroupPicker
|
||||||
key={"name"}
|
labelKey="name"
|
||||||
title={"User group"}
|
|
||||||
bind:searchTerm
|
bind:searchTerm
|
||||||
bind:selected={selectedGroups}
|
list={filteredGroups}
|
||||||
bind:filtered={filteredGroups}
|
selected={user.userGroups}
|
||||||
{addAll}
|
on:select={e => addGroup(e.detail)}
|
||||||
select={addGroup}
|
on:deselect={e => removeGroup(e.detail)}
|
||||||
/>
|
/>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
|
@ -325,7 +302,10 @@
|
||||||
on:click={() => $goto(`../groups/${group._id}`)}
|
on:click={() => $goto(`../groups/${group._id}`)}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
on:click={removeGroup(group._id)}
|
on:click={e => {
|
||||||
|
removeGroup(group._id)
|
||||||
|
e.stopPropagation()
|
||||||
|
}}
|
||||||
hoverable
|
hoverable
|
||||||
size="S"
|
size="S"
|
||||||
name="Close"
|
name="Close"
|
||||||
|
@ -370,13 +350,10 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Modal bind:this={deleteModal}>
|
<Modal bind:this={deleteModal}>
|
||||||
<DeleteUserModal user={$userFetch.data} />
|
<DeleteUserModal {user} />
|
||||||
</Modal>
|
</Modal>
|
||||||
<Modal bind:this={resetPasswordModal}>
|
<Modal bind:this={resetPasswordModal}>
|
||||||
<ForceResetPasswordModal
|
<ForceResetPasswordModal {user} on:update={fetchUser} />
|
||||||
user={$userFetch.data}
|
|
||||||
on:update={userFetch.refresh}
|
|
||||||
/>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { writable, get } from "svelte/store"
|
import { writable, get } from "svelte/store"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { auth } from "stores/portal"
|
import { auth, users } from "stores/portal"
|
||||||
|
|
||||||
export function createGroupsStore() {
|
export function createGroupsStore() {
|
||||||
const store = writable([])
|
const store = writable([])
|
||||||
|
@ -10,8 +10,8 @@ export function createGroupsStore() {
|
||||||
// only init if these is a groups license, just to be sure but the feature will be blocked
|
// only init if these is a groups license, just to be sure but the feature will be blocked
|
||||||
// on the backend anyway
|
// on the backend anyway
|
||||||
if (get(auth).groupsEnabled) {
|
if (get(auth).groupsEnabled) {
|
||||||
const users = await API.getGroups()
|
const groups = await API.getGroups()
|
||||||
store.set(users)
|
store.set(groups)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -41,6 +41,55 @@ export function createGroupsStore() {
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addUser: async (groupId, userId) => {
|
||||||
|
// Sanity check
|
||||||
|
const user = await users.get(userId)
|
||||||
|
const group = get(store).find(x => x._id === groupId)
|
||||||
|
if (!group?._id || !user?._id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check we haven't already been added
|
||||||
|
if (group.users?.find(x => x._id === userId)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update group
|
||||||
|
await actions.save({
|
||||||
|
...group,
|
||||||
|
users: [...group.users, { _id: userId, email: user.email }],
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update user
|
||||||
|
let userGroups = user.userGroups || []
|
||||||
|
userGroups.push(groupId)
|
||||||
|
await users.save({
|
||||||
|
...user,
|
||||||
|
userGroups,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
removeUser: async (groupId, userId) => {
|
||||||
|
// Sanity check
|
||||||
|
const user = await users.get(userId)
|
||||||
|
const group = get(store).find(x => x._id === groupId)
|
||||||
|
if (!group?._id || !user?._id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update group
|
||||||
|
await actions.save({
|
||||||
|
...group,
|
||||||
|
users: group.users.filter(x => x._id !== userId),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update user
|
||||||
|
await users.save({
|
||||||
|
...user,
|
||||||
|
userGroups: user.userGroups.filter(x => x !== groupId),
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
Loading…
Reference in New Issue