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

View File

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