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,6 +134,7 @@
</List> </List>
{/if} {/if}
{#if appUsers.length} {#if appUsers.length}
<div>
<List title="Users"> <List title="Users">
{#each appUsers as user} {#each appUsers as user}
<ListItem title={user.email} avatar> <ListItem title={user.email} avatar>
@ -207,19 +158,14 @@
</List> </List>
<div class="pagination"> <div class="pagination">
<Pagination <Pagination
page={$pageInfo.pageNumber} page={$usersFetch.pageNumber + 1}
hasPrevPage={$pageInfo.loading ? false : $pageInfo.hasPrevPage} hasPrevPage={$usersFetch.hasPrevPage}
hasNextPage={$pageInfo.loading ? false : $pageInfo.hasNextPage} hasNextPage={$usersFetch.hasNextPage}
goToPrevPage={async () => { goToPrevPage={$usersFetch.loading ? null : fetch.prevPage}
await pageInfo.prevPage() goToNextPage={$usersFetch.loading ? null : fetch.nextPage}
fetchUsers(page, search)
}}
goToNextPage={async () => {
await pageInfo.nextPage()
fetchUsers(page, search)
}}
/> />
</div> </div>
</div>
{/if} {/if}
{:else} {:else}
<div class="align"> <div class="align">
@ -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",
},
options: {
query: {
email: "",
},
},
})
$: page = $pageInfo.page let search = ""
$: fetchUsers(page, search) let data = [{ id: "", role: "" }]
$: availableUsers = getAvailableUsers($users, appUsers, appData)
$: filteredGroups = $groups.filter(group => { $: usersFetch.update({
return !group.apps.find(appId => { query: {
return appId === app.appId email: search,
},
}) })
}) $: fixedAppId = apps.getProdAppID(app.devId)
$: valid = $: availableUsers = getAvailableUsers($usersFetch.rows, appUsers, data)
appData?.length && !appData?.some(x => !x.id?.length || !x.role?.length) $: 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
if (group.apps.includes(appId)) {
return false
} }
// need to remove the page if they've started searching
if (search && !prevSearch) { // Filter out new groups which are going to be assigned
pageInfo.reset() if (newGroups.find(x => x.id === group._id)) {
page = undefined return false
}
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")
} }
// Match search string
return !search || group.name.toLowerCase().includes(search)
})
} }
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