Fix assignment modal, use userFetch for searching users, fix data mutations

This commit is contained in:
Andrew Kingston 2022-08-08 13:20:59 +01:00
parent dbdfb9cb83
commit 3320a33b9b
2 changed files with 154 additions and 155 deletions

View File

@ -16,54 +16,34 @@
import RoleSelect from "components/common/RoleSelect.svelte"
import { users, groups, apps, auth } from "stores/portal"
import AssignmentModal from "./AssignmentModal.svelte"
import { createPaginationStore } from "helpers/pagination"
import { roles } from "stores/backend"
import { API } from "api"
import { fetchData } from "@budibase/frontend-core"
export let app
const usersFetch = fetchData({
API,
datasource: {
type: "user",
},
options: {
query: {
appId: apps.getProdAppID(app.devId),
},
},
})
let assignmentModal
let appGroups = []
let appUsers = []
let prevSearch = undefined,
search = undefined
let pageInfo = createPaginationStore()
let fixedAppId
$: page = $pageInfo.page
$: fixedAppId = apps.getProdAppID(app.devId)
$: appUsers = $usersFetch.rows
$: appGroups = $groups.filter(x => {
return x.apps.includes(app.appId)
})
async function addData(appData) {
let gr_prefix = "gr"
let us_prefix = "us"
appData.forEach(async data => {
if (data.id.startsWith(gr_prefix)) {
let matchedGroup = $groups.find(group => {
return group._id === data.id
})
matchedGroup.apps.push(app.appId)
matchedGroup.roles[fixedAppId] = data.role
groups.actions.save(matchedGroup)
} else if (data.id.startsWith(us_prefix)) {
let matchedUser = $users.data.find(user => {
return user._id === data.id
})
let newUser = {
...matchedUser,
roles: { [fixedAppId]: data.role, ...matchedUser.roles },
}
await users.save(newUser, { opts: { appId: fixedAppId } })
await fetchUsers(page, search)
}
})
await groups.actions.init()
}
async function removeUser(user) {
// Remove the user role
const filteredRoles = { ...user.roles }
@ -74,65 +54,35 @@
...filteredRoles,
},
})
await fetchUsers(page, search)
await usersFetch.refresh()
}
async function removeGroup(group) {
// Remove the user role
let filteredApps = group.apps.filter(
const filteredApps = group.apps.filter(
x => apps.extractAppId(x) !== app.appId
)
const filteredRoles = { ...group.roles }
delete filteredRoles[fixedAppId]
await groups.actions.save({
...group,
apps: filteredApps,
roles: { ...filteredRoles },
})
await fetchUsers(page, search)
await usersFetch.refresh()
}
async function updateUserRole(role, user) {
user.roles[fixedAppId] = role
users.save(user)
await users.save(user)
}
async function updateGroupRole(role, group) {
group.roles[fixedAppId] = role
groups.actions.save(group)
}
async function fetchUsers(page, search) {
if ($pageInfo.loading) {
return
}
// need to remove the page if they've started searching
if (search && !prevSearch) {
pageInfo.reset()
page = undefined
}
prevSearch = search
try {
pageInfo.loading()
await users.search({ page, appId: fixedAppId })
pageInfo.fetched($users.hasNextPage, $users.nextPage)
appUsers =
$users.data?.filter(x => {
return Object.keys(x.roles).find(y => {
return y === fixedAppId
})
}) || []
} catch (error) {
notifications.error("Error getting user list")
}
await groups.actions.save(group)
}
onMount(async () => {
try {
await fetchUsers(page, search)
await groups.actions.init()
await apps.load()
await roles.fetch()
@ -149,11 +99,11 @@
<Heading>Access</Heading>
<div class="subtitle">
<Body size="S">
Assign users to your app and define their access here</Body
>
<Button on:click={assignmentModal.show} icon="User" cta
>Assign users</Button
>
Assign users to your app and define their access here
</Body>
<Button on:click={assignmentModal.show} icon="User" cta>
Assign users
</Button>
</div>
</div>
{#if $auth.groupsEnabled && appGroups.length}
@ -184,41 +134,37 @@
</List>
{/if}
{#if appUsers.length}
<List title="Users">
{#each appUsers as user}
<ListItem title={user.email} avatar>
<RoleSelect
on:change={e => updateUserRole(e.detail, user)}
autoWidth
quiet
value={user.roles[
Object.keys(user.roles).find(x => x === fixedAppId)
]}
allowPublic={false}
/>
<Icon
on:click={() => removeUser(user)}
hoverable
size="S"
name="Close"
/>
</ListItem>
{/each}
</List>
<div class="pagination">
<Pagination
page={$pageInfo.pageNumber}
hasPrevPage={$pageInfo.loading ? false : $pageInfo.hasPrevPage}
hasNextPage={$pageInfo.loading ? false : $pageInfo.hasNextPage}
goToPrevPage={async () => {
await pageInfo.prevPage()
fetchUsers(page, search)
}}
goToNextPage={async () => {
await pageInfo.nextPage()
fetchUsers(page, search)
}}
/>
<div>
<List title="Users">
{#each appUsers as user}
<ListItem title={user.email} avatar>
<RoleSelect
on:change={e => updateUserRole(e.detail, user)}
autoWidth
quiet
value={user.roles[
Object.keys(user.roles).find(x => x === fixedAppId)
]}
allowPublic={false}
/>
<Icon
on:click={() => removeUser(user)}
hoverable
size="S"
name="Close"
/>
</ListItem>
{/each}
</List>
<div class="pagination">
<Pagination
page={$usersFetch.pageNumber + 1}
hasPrevPage={$usersFetch.hasPrevPage}
hasNextPage={$usersFetch.hasNextPage}
goToPrevPage={$usersFetch.loading ? null : fetch.prevPage}
goToNextPage={$usersFetch.loading ? null : fetch.nextPage}
/>
</div>
</div>
{/if}
{:else}
@ -246,7 +192,7 @@
</div>
<Modal bind:this={assignmentModal}>
<AssignmentModal {app} {appUsers} {addData} />
<AssignmentModal {app} {appUsers} on:update={usersFetch.refresh} />
</Modal>
<style>

View File

@ -5,37 +5,46 @@
ActionButton,
Layout,
Icon,
notifications,
} from "@budibase/bbui"
import { roles } from "stores/backend"
import { groups, users, auth } from "stores/portal"
import { Constants, RoleUtils } from "@budibase/frontend-core"
import { createPaginationStore } from "helpers/pagination"
import { groups, users, auth, apps } from "stores/portal"
import { Constants, RoleUtils, fetchData } from "@budibase/frontend-core"
import { API } from "api"
import { createEventDispatcher } from "svelte"
export let app
export let addData
export let appUsers = []
let prevSearch = undefined,
search = undefined
let pageInfo = createPaginationStore()
let appData = [{ id: "", role: "" }]
$: page = $pageInfo.page
$: fetchUsers(page, search)
$: availableUsers = getAvailableUsers($users, appUsers, appData)
$: filteredGroups = $groups.filter(group => {
return !group.apps.find(appId => {
return appId === app.appId
})
const dispatch = createEventDispatcher()
const usersFetch = fetchData({
API,
datasource: {
type: "user",
},
options: {
query: {
email: "",
},
},
})
$: valid =
appData?.length && !appData?.some(x => !x.id?.length || !x.role?.length)
let search = ""
let data = [{ id: "", role: "" }]
$: usersFetch.update({
query: {
email: search,
},
})
$: fixedAppId = apps.getProdAppID(app.devId)
$: availableUsers = getAvailableUsers($usersFetch.rows, appUsers, data)
$: availableGroups = getAvailableGroups($groups, app.appId, search, data)
$: valid = data?.length && !data?.some(x => !x.id?.length || !x.role?.length)
$: optionSections = {
...($auth.groupsEnabled &&
filteredGroups.length && {
availableGroups.length && {
["User groups"]: {
data: filteredGroups,
data: availableGroups,
getLabel: group => group.name,
getValue: group => group._id,
getIcon: group => group.icon,
@ -51,8 +60,53 @@
},
}
const addData = async appData => {
const gr_prefix = "gr"
const us_prefix = "us"
for (let data of appData) {
// Assign group
if (data.id.startsWith(gr_prefix)) {
const group = $groups.find(group => {
return group._id === data.id
})
if (!group) {
continue
}
await groups.actions.save({
...group,
apps: [...group.apps, app.appId],
roles: {
...group.roles,
[fixedAppId]: data.role,
},
})
}
// Assign user
else if (data.id.startsWith(us_prefix)) {
const user = await users.get(data.id)
await users.save({
...user,
roles: {
...user.roles,
[fixedAppId]: data.role,
},
})
}
}
// Refresh data when completed
await usersFetch.refresh()
await groups.actions.init()
dispatch("update")
}
const getAvailableUsers = (allUsers, appUsers, newUsers) => {
return (allUsers.data || []).filter(user => {
return (allUsers || []).filter(user => {
// Filter out admin users
if (user?.admin?.global || user?.builder?.global) {
return false
}
// Filter out assigned users
if (appUsers.find(x => x._id === user._id)) {
return false
@ -63,31 +117,30 @@
})
}
async function fetchUsers(page, search) {
if ($pageInfo.loading) {
return
}
// need to remove the page if they've started searching
if (search && !prevSearch) {
pageInfo.reset()
page = undefined
}
prevSearch = search
try {
pageInfo.loading()
await users.search({ page, email: search })
pageInfo.fetched($users.hasNextPage, $users.nextPage)
} catch (error) {
notifications.error("Error getting user list")
}
const getAvailableGroups = (allGroups, appId, search, newGroups) => {
search = search?.toLowerCase()
return (allGroups || []).filter(group => {
// Filter out assigned groups
if (group.apps.includes(appId)) {
return false
}
// Filter out new groups which are going to be assigned
if (newGroups.find(x => x.id === group._id)) {
return false
}
// Match search string
return !search || group.name.toLowerCase().includes(search)
})
}
function addNewInput() {
appData = [...appData, { id: "", role: "" }]
data = [...data, { id: "", role: "" }]
}
const removeItem = index => {
appData = appData.filter((x, idx) => idx !== index)
data = data.filter((x, idx) => idx !== index)
}
</script>
@ -96,13 +149,13 @@
title="Assign users to your app"
confirmText="Done"
cancelText="Cancel"
onConfirm={() => addData(appData)}
onConfirm={() => addData(data)}
showCloseIcon={false}
disabled={!valid}
>
{#if appData?.length}
{#if data.length}
<Layout noPadding gap="XS">
{#each appData as input, index}
{#each data as input, index}
<div class="item">
<div class="picker">
<PickerDropdown