Fix multiple issues in user details page

This commit is contained in:
Andrew Kingston 2022-08-04 15:33:51 +01:00
parent ea6356147b
commit f5f510a482
4 changed files with 115 additions and 134 deletions

View File

@ -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,
} }
}) })
} }

View File

@ -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

View File

@ -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>

View File

@ -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 {