Fix assignment modal, use userFetch for searching users, fix data mutations
This commit is contained in:
parent
e18e941449
commit
8d13928d67
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue