diff --git a/packages/bbui/src/Form/Core/InputDropdown.svelte b/packages/bbui/src/Form/Core/InputDropdown.svelte
new file mode 100644
index 0000000000..c9ca70dff9
--- /dev/null
+++ b/packages/bbui/src/Form/Core/InputDropdown.svelte
@@ -0,0 +1,215 @@
+
+
+
+
+
diff --git a/packages/bbui/src/Form/Core/Multiselect.svelte b/packages/bbui/src/Form/Core/Multiselect.svelte
index 3eb1add267..9dd5a25a4f 100644
--- a/packages/bbui/src/Form/Core/Multiselect.svelte
+++ b/packages/bbui/src/Form/Core/Multiselect.svelte
@@ -13,6 +13,7 @@
export let readonly = false
export let autocomplete = false
export let sort = false
+ export let autoWidth = false
const dispatch = createEventDispatcher()
$: selectedLookupMap = getSelectedLookupMap(value)
@@ -85,4 +86,5 @@
{getOptionValue}
onSelectOption={toggleOption}
{sort}
+ {autoWidth}
/>
diff --git a/packages/bbui/src/Form/InputDropdown.svelte b/packages/bbui/src/Form/InputDropdown.svelte
new file mode 100644
index 0000000000..73516ea37c
--- /dev/null
+++ b/packages/bbui/src/Form/InputDropdown.svelte
@@ -0,0 +1,55 @@
+
+
+
+
+
diff --git a/packages/bbui/src/Form/Multiselect.svelte b/packages/bbui/src/Form/Multiselect.svelte
index 957dcccddf..7bcf22aa06 100644
--- a/packages/bbui/src/Form/Multiselect.svelte
+++ b/packages/bbui/src/Form/Multiselect.svelte
@@ -14,7 +14,7 @@
export let getOptionLabel = option => option
export let getOptionValue = option => option
export let sort = false
-
+ export let autoWidth = false
const dispatch = createEventDispatcher()
const onChange = e => {
value = e.detail
@@ -33,6 +33,7 @@
{sort}
{getOptionLabel}
{getOptionValue}
+ {autoWidth}
on:change={onChange}
on:click
/>
diff --git a/packages/bbui/src/Table/Table.svelte b/packages/bbui/src/Table/Table.svelte
index 29d9772d33..b6dea0638b 100644
--- a/packages/bbui/src/Table/Table.svelte
+++ b/packages/bbui/src/Table/Table.svelte
@@ -445,7 +445,7 @@
width: 100%;
border-radius: 0;
display: grid;
- overflow: auto;
+ overflow: visible;
}
/* Header */
@@ -513,7 +513,7 @@
z-index: 3;
}
.spectrum-Table-headCell .title {
- overflow: hidden;
+ overflow: visible;
text-overflow: ellipsis;
}
.spectrum-Table-headCell:hover .spectrum-Table-editIcon {
diff --git a/packages/bbui/src/index.js b/packages/bbui/src/index.js
index ea7dd88020..6a4373df92 100644
--- a/packages/bbui/src/index.js
+++ b/packages/bbui/src/index.js
@@ -23,6 +23,7 @@ export { default as Icon, directions } from "./Icon/Icon.svelte"
export { default as Toggle } from "./Form/Toggle.svelte"
export { default as RadioGroup } from "./Form/RadioGroup.svelte"
export { default as Checkbox } from "./Form/Checkbox.svelte"
+export { default as InputDropdown } from "./Form/InputDropdown.svelte"
export { default as DetailSummary } from "./DetailSummary/DetailSummary.svelte"
export { default as Popover } from "./Popover/Popover.svelte"
export { default as ProgressBar } from "./ProgressBar/ProgressBar.svelte"
@@ -71,6 +72,7 @@ export { default as ListItem } from "./List/ListItem.svelte"
// Renderers
export { default as BoldRenderer } from "./Table/BoldRenderer.svelte"
export { default as CodeRenderer } from "./Table/CodeRenderer.svelte"
+export { default as InternalRenderer } from "./Table/InternalRenderer.svelte"
// Typography
export { default as Body } from "./Typography/Body.svelte"
diff --git a/packages/builder/src/components/settings/UserGroupPicker.svelte b/packages/builder/src/components/settings/UserGroupPicker.svelte
new file mode 100644
index 0000000000..8e67331b09
--- /dev/null
+++ b/packages/builder/src/components/settings/UserGroupPicker.svelte
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+ {#each filtered as item}
+
+
+ {item[key]}
+
+
+ {#if selected.includes(item._id)}
+
+
+
+ {/if}
+
+ {/each}
+
+
+
+
diff --git a/packages/builder/src/pages/builder/portal/manage/_layout.svelte b/packages/builder/src/pages/builder/portal/manage/_layout.svelte
index b6c2897730..a63195a214 100644
--- a/packages/builder/src/pages/builder/portal/manage/_layout.svelte
+++ b/packages/builder/src/pages/builder/portal/manage/_layout.svelte
@@ -12,7 +12,7 @@
$: wide =
$page.path.includes("email/:template") ||
- $page.path.includes("users") ||
+ ($page.path.includes("users") && !$page.path.includes(":userId")) ||
($page.path.includes("groups") && !$page.path.includes(":groupId"))
diff --git a/packages/builder/src/pages/builder/portal/manage/groups/[groupId].svelte b/packages/builder/src/pages/builder/portal/manage/groups/[groupId].svelte
index b3ee52ffcf..08610e0e23 100644
--- a/packages/builder/src/pages/builder/portal/manage/groups/[groupId].svelte
+++ b/packages/builder/src/pages/builder/portal/manage/groups/[groupId].svelte
@@ -8,14 +8,13 @@
Body,
Icon,
Popover,
- Search,
- Divider,
- Detail,
notifications,
List,
ListItem,
StatusLight,
} from "@budibase/bbui"
+ import UserGroupPicker from "components/settings/UserGroupPicker.svelte"
+
import { users, apps, groups } from "stores/portal"
import { onMount } from "svelte"
import { RoleUtils } from "@budibase/frontend-core"
@@ -47,8 +46,9 @@
}
async function selectUser(id) {
- let selectedUser = selectedUsers.find(user_id => user_id === id)
+ let selectedUser = selectedUsers.includes(id)
let enrichedUser = $users.find(user => user._id === id)
+
if (selectedUser) {
selectedUsers = selectedUsers.filter(id => id !== selectedUser)
let newUsers = group.users.filter(user => user._id !== id)
@@ -57,6 +57,7 @@
selectedUsers = [...selectedUsers, id]
group.users.push(enrichedUser)
}
+
await groups.actions.save(group)
}
@@ -97,55 +98,24 @@
Add User
-
-
-
-
-
- {#each filteredUsers as user}
-
-
- {user.email}
-
-
- {#if selectedUsers.includes(user._id)}
-
-
-
- {/if}
-
- {/each}
-
-
+
{#if group?.users.length}
{#each group.users as user}
- removeUser(user._id)}
+ on:click={() => removeUser(user?._id)}
hoverable
size="L"
name="Close"
@@ -180,7 +150,7 @@
{/each}
{:else}
-
+
{/if}
@@ -190,24 +160,6 @@
margin-left: var(--spacing-l);
}
- .users-header {
- align-items: center;
- padding: var(--spacing-m) 0 var(--spacing-m) 0;
- display: flex;
- justify-content: space-between;
- }
-
- .user-selection {
- align-items: end;
- display: flex;
- justify-content: space-between;
- cursor: pointer;
- }
-
- .user-selection > :first-child {
- padding-top: var(--spacing-m);
- }
-
.header {
display: flex;
justify-content: space-between;
diff --git a/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte b/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte
index a8cb340465..f095cf8a90 100644
--- a/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte
+++ b/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte
@@ -2,43 +2,44 @@
import { goto } from "@roxi/routify"
import {
ActionButton,
+ ActionMenu,
+ Avatar,
Button,
Layout,
Heading,
Body,
- Divider,
Label,
+ List,
+ ListItem,
+ Icon,
Input,
+ MenuItem,
+ Popover,
Select,
- Toggle,
Modal,
- Table,
ModalContent,
notifications,
+ StatusLight,
} from "@budibase/bbui"
+ import { onMount } from "svelte"
+
import { fetchData } from "helpers"
- import { users, auth } from "stores/portal"
+ import { users, auth, groups } from "stores/portal"
+ import { Constants } from "@budibase/frontend-core"
- import TagsRenderer from "./_components/RolesTagsTableRenderer.svelte"
-
- import UpdateRolesModal from "./_components/UpdateRolesModal.svelte"
import ForceResetPasswordModal from "./_components/ForceResetPasswordModal.svelte"
+ import { RoleUtils } from "@budibase/frontend-core"
+ import UserGroupPicker from "components/settings/UserGroupPicker.svelte"
export let userId
let deleteUserModal
- let editRolesModal
let resetPasswordModal
-
- const roleSchema = {
- name: { displayName: "App" },
- role: {},
- }
-
- const noRoleSchema = {
- name: { displayName: "App" },
- }
-
+ let popoverAnchor
+ let searchTerm = ""
+ let popover
+ let selectedGroups = []
$: defaultRoleId = $userFetch?.data?.builder?.global ? "ADMIN" : ""
+
// Merge the Apps list and the roles response to get something that makes sense for the table
$: allAppList = Object.keys($apps?.data).map(id => {
const roleId = $userFetch?.data?.roles?.[id] || defaultRoleId
@@ -50,19 +51,23 @@
}
})
- $: appList = allAppList.filter(app => !!app.role[0])
- $: noRoleAppList = allAppList
- .filter(app => !app.role[0])
- .map(app => {
- delete app.role
- return app
- })
+ // Used for searching through groups in the add group popover
+ $: filteredGroups = $groups.filter(
+ group =>
+ selectedGroups &&
+ group?.name?.toLowerCase().includes(searchTerm.toLowerCase())
+ )
- let selectedApp
+ $: appList = allAppList.filter(app => !!app.role[0])
+
+ $: userGroups = $groups.filter(x => {
+ return x.users?.some(y => {
+ return y._id === userId
+ })
+ })
const userFetch = fetchData(`/api/global/users/${userId}`)
const apps = fetchData(`/api/global/roles`)
-
async function deleteUser() {
try {
await users.delete(userId)
@@ -73,8 +78,17 @@
}
}
- let toggleDisabled = false
-
+ function getHighestRole(roles) {
+ let highestRole
+ let highestRoleNumber = 0
+ roles.forEach(role => {
+ let roleNumber = RoleUtils.getRolePriority(role._id)
+ if (roleNumber > highestRoleNumber) {
+ highestRole = role
+ }
+ })
+ return highestRole
+ }
async function updateUserFirstName(evt) {
try {
await users.save({ ...$userFetch?.data, firstName: evt.target.value })
@@ -84,6 +98,13 @@
}
}
+ 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) {
try {
await users.save({ ...$userFetch?.data, lastName: evt.target.value })
@@ -93,6 +114,30 @@
}
}
+ async function updateUserRole() {
+ return
+ }
+
+ async function addGroup(groupId) {
+ let selectedGroup = selectedGroups.includes(groupId)
+ let newUser = $users.find(user => user._id === userId)
+ let group = $groups.find(group => group._id === groupId)
+
+ if (selectedGroup) {
+ selectedGroups = selectedGroups.filter(id => id === selectedGroup)
+ let newUsers = group.users.filter(user => user._id !== newUser._id)
+ group.users = newUsers
+ } else {
+ selectedGroups = [...selectedGroups, groupId]
+ group.users.push(newUser)
+ }
+
+ await groups.actions.save(group)
+ }
+
+ function addAll() {}
+
+ /*
async function toggleFlag(flagName, detail) {
toggleDisabled = true
try {
@@ -104,6 +149,7 @@
toggleDisabled = false
}
+
async function toggleBuilderAccess({ detail }) {
return toggleFlag("builder", detail)
}
@@ -116,38 +162,56 @@
selectedApp = detail
editRolesModal.show()
}
+*/
+ onMount(async () => {
+ try {
+ await groups.actions.init()
+ } catch (error) {
+ notifications.error("Error getting User groups")
+ }
+ })
-
+
-
$goto("./")}
- quiet
- size="S"
- icon="BackAndroid"
- >
- Back to users
+ $goto("./")} size="S" icon="ArrowLeft">
+ Back
- User: {$userFetch?.data?.email}
-
- Change user settings and update their app roles. Also contains the ability
- to delete the user as well as force reset their password.
-
-
+
+
+
+
+
+
+ {$userFetch?.data?.firstName +
+ " " +
+ $userFetch?.data?.lastName}
+ {$userFetch?.data?.email}
+
+
+
+
+
+
+
+
+ Force Password Reset
+ Delete
+
+
+
+
- General
-
- Email
-
-
-
- Group(s)
-
-
First name
{#if userId !== $auth.user._id}
- Development access
-
-
-
- Administration access
-
+ Role
+
{/if}
-
-
-
- Configure roles
- Specify a role to grant access to an app.
-
-
-
- No Access
- Apps do not appear in the users portal. Public pages may still be viewed
- if visited directly.
-
-
-
+
+
- Delete user
- Deleting a user completely removes them from your account.
+
+
+ User groups
+ Manage apps that this User group has been assigned to
+
+
+ Add User Group
+
+
+
+
+
+
+
+ {#if userGroups.length}
+ {#each userGroups as group}
+
+ {/each}
+ {:else}
+
+ {/if}
+
+
+
+
+
+
+
Apps
+
+ Manage apps that this User group has been assigned to
+
+
+
+
+ {#if appList.length}
+ {#each appList as app}
+
+
+
+
+ {getHighestRole(app.role).name}
+
+
+
+ {/each}
+ {:else}
+
+ {/if}
+
-
- Delete user
-
@@ -248,13 +332,6 @@