Update user and group details pages with new tables
This commit is contained in:
parent
e91ed89d72
commit
3dd5916f24
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import Field from "./Field.svelte"
|
import Field from "./Field.svelte"
|
||||||
import Select from "./Core/Select.svelte"
|
import Select from "./Core/Select.svelte"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
export let value = null
|
export let value = null
|
||||||
export let label = undefined
|
export let label = undefined
|
||||||
|
@ -20,6 +21,12 @@
|
||||||
export let sort = false
|
export let sort = false
|
||||||
export let tooltip = ""
|
export let tooltip = ""
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
const onChange = e => {
|
||||||
|
value = e.detail
|
||||||
|
dispatch("change", e.detail)
|
||||||
|
}
|
||||||
|
|
||||||
const extractProperty = (value, property) => {
|
const extractProperty = (value, property) => {
|
||||||
if (value && typeof value === "object") {
|
if (value && typeof value === "object") {
|
||||||
return value[property]
|
return value[property]
|
||||||
|
@ -44,7 +51,7 @@
|
||||||
{getOptionIcon}
|
{getOptionIcon}
|
||||||
{getOptionColour}
|
{getOptionColour}
|
||||||
{isOptionEnabled}
|
{isOptionEnabled}
|
||||||
on:change
|
on:change={onChange}
|
||||||
on:click
|
on:click
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
quiet
|
quiet
|
||||||
allowRemove
|
allowRemove
|
||||||
allowPublic={false}
|
allowPublic={false}
|
||||||
on:change={e => rolesContext.updateUserRole(e.detail, row._id)}
|
on:change={e => rolesContext.updateRole(e.detail, row._id)}
|
||||||
on:remove={() => rolesContext.removeUserRole(row._id)}
|
on:remove={() => rolesContext.removeRole(row._id)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
<script>
|
|
||||||
import RoleSelect from "components/common/RoleSelect.svelte"
|
|
||||||
import { getContext } from "svelte"
|
|
||||||
|
|
||||||
const rolesContext = getContext("roles")
|
|
||||||
|
|
||||||
export let value
|
|
||||||
export let row
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<RoleSelect
|
|
||||||
{value}
|
|
||||||
quiet
|
|
||||||
allowRemove
|
|
||||||
allowPublic={false}
|
|
||||||
on:change={e => rolesContext.updateGroupRole(e.detail, row._id)}
|
|
||||||
on:remove={() => rolesContext.removeGroupRole(row._id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
div {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -16,15 +16,14 @@
|
||||||
import { roles } from "stores/backend"
|
import { roles } from "stores/backend"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { fetchData } from "@budibase/frontend-core"
|
import { fetchData } from "@budibase/frontend-core"
|
||||||
import UserRoleRenderer from "./_components/UserRoleRenderer.svelte"
|
import EditableRoleRenderer from "./_components/EditableRoleRenderer.svelte"
|
||||||
import GroupRoleRenderer from "./_components/GroupRoleRenderer.svelte"
|
|
||||||
|
|
||||||
const userSchema = {
|
const userSchema = {
|
||||||
email: {
|
email: {
|
||||||
type: "string",
|
type: "string",
|
||||||
width: "1fr",
|
width: "1fr",
|
||||||
},
|
},
|
||||||
userAppRole: {
|
role: {
|
||||||
displayName: "Access",
|
displayName: "Access",
|
||||||
width: "150px",
|
width: "150px",
|
||||||
borderLeft: true,
|
borderLeft: true,
|
||||||
|
@ -35,7 +34,7 @@
|
||||||
type: "string",
|
type: "string",
|
||||||
width: "1fr",
|
width: "1fr",
|
||||||
},
|
},
|
||||||
groupAppRole: {
|
role: {
|
||||||
displayName: "Access",
|
displayName: "Access",
|
||||||
width: "150px",
|
width: "150px",
|
||||||
borderLeft: true,
|
borderLeft: true,
|
||||||
|
@ -43,12 +42,8 @@
|
||||||
}
|
}
|
||||||
const customRenderers = [
|
const customRenderers = [
|
||||||
{
|
{
|
||||||
column: "userAppRole",
|
column: "role",
|
||||||
component: UserRoleRenderer,
|
component: EditableRoleRenderer,
|
||||||
},
|
|
||||||
{
|
|
||||||
column: "groupAppRole",
|
|
||||||
component: GroupRoleRenderer,
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -76,7 +71,7 @@
|
||||||
const getAppUsers = (users, appId) => {
|
const getAppUsers = (users, appId) => {
|
||||||
return users.map(user => ({
|
return users.map(user => ({
|
||||||
...user,
|
...user,
|
||||||
userAppRole: user.roles[Object.keys(user.roles).find(x => x === appId)],
|
role: user.roles[Object.keys(user.roles).find(x => x === appId)],
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,13 +85,30 @@
|
||||||
})
|
})
|
||||||
.map(group => ({
|
.map(group => ({
|
||||||
...group,
|
...group,
|
||||||
groupAppRole:
|
role: group.roles[
|
||||||
group.roles[
|
|
||||||
groups.actions.getGroupAppIds(group).find(x => x === appId)
|
groups.actions.getGroupAppIds(group).find(x => x === appId)
|
||||||
],
|
],
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateRole = async (role, id) => {
|
||||||
|
// Check if this is a user or a group
|
||||||
|
if ($usersFetch.rows.some(user => user._id === id)) {
|
||||||
|
await updateUserRole(role, id)
|
||||||
|
} else {
|
||||||
|
await updateGroupRole(role, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeRole = async id => {
|
||||||
|
// Check if this is a user or a group
|
||||||
|
if ($usersFetch.rows.some(user => user._id === id)) {
|
||||||
|
await removeUserRole(id)
|
||||||
|
} else {
|
||||||
|
await removeGroupRole(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const updateUserRole = async (role, userId) => {
|
const updateUserRole = async (role, userId) => {
|
||||||
const user = $usersFetch.rows.find(user => user._id === userId)
|
const user = $usersFetch.rows.find(user => user._id === userId)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
@ -142,10 +154,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
setContext("roles", {
|
setContext("roles", {
|
||||||
updateUserRole,
|
updateRole,
|
||||||
removeUserRole,
|
removeRole,
|
||||||
updateGroupRole,
|
|
||||||
removeGroupRole,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
import { url, isActive } from "@roxi/routify"
|
import { url, isActive } from "@roxi/routify"
|
||||||
import { Page } from "@budibase/bbui"
|
import { Page } from "@budibase/bbui"
|
||||||
import { Content, SideNav, SideNavItem } from "components/portal/page"
|
import { Content, SideNav, SideNavItem } from "components/portal/page"
|
||||||
|
|
||||||
|
$: narrow = !$isActive("./users/index") && !$isActive("./groups/index")
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Page narrow>
|
<Page narrow>
|
||||||
|
|
|
@ -7,10 +7,7 @@
|
||||||
Icon,
|
Icon,
|
||||||
Popover,
|
Popover,
|
||||||
notifications,
|
notifications,
|
||||||
List,
|
Table,
|
||||||
ListItem,
|
|
||||||
StatusLight,
|
|
||||||
Divider,
|
|
||||||
ActionMenu,
|
ActionMenu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
Modal,
|
Modal,
|
||||||
|
@ -18,32 +15,76 @@
|
||||||
import UserGroupPicker from "components/settings/UserGroupPicker.svelte"
|
import UserGroupPicker from "components/settings/UserGroupPicker.svelte"
|
||||||
import { createPaginationStore } from "helpers/pagination"
|
import { createPaginationStore } from "helpers/pagination"
|
||||||
import { users, apps, groups } from "stores/portal"
|
import { users, apps, groups } from "stores/portal"
|
||||||
import { onMount } from "svelte"
|
import { onMount, setContext } from "svelte"
|
||||||
import { RoleUtils } from "@budibase/frontend-core"
|
|
||||||
import { roles } from "stores/backend"
|
import { roles } from "stores/backend"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
import CreateEditGroupModal from "./_components/CreateEditGroupModal.svelte"
|
import CreateEditGroupModal from "./_components/CreateEditGroupModal.svelte"
|
||||||
import GroupIcon from "./_components/GroupIcon.svelte"
|
import GroupIcon from "./_components/GroupIcon.svelte"
|
||||||
import AppAddModal from "./_components/AppAddModal.svelte"
|
|
||||||
import { Breadcrumbs, Breadcrumb } from "components/portal/page"
|
import { Breadcrumbs, Breadcrumb } from "components/portal/page"
|
||||||
|
import AppNameTableRenderer from "../users/_components/AppNameTableRenderer.svelte"
|
||||||
|
import RemoveUserTableRenderer from "./_components/RemoveUserTableRenderer.svelte"
|
||||||
|
import AppRoleTableRenderer from "../users/_components/AppRoleTableRenderer.svelte"
|
||||||
|
|
||||||
export let groupId
|
export let groupId
|
||||||
|
|
||||||
|
const userSchema = {
|
||||||
|
email: {
|
||||||
|
width: "1fr",
|
||||||
|
},
|
||||||
|
_id: {
|
||||||
|
displayName: "",
|
||||||
|
width: "auto",
|
||||||
|
borderLeft: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const appSchema = {
|
||||||
|
name: {
|
||||||
|
width: "2fr",
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
width: "1fr",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const customUserTableRenderers = [
|
||||||
|
{
|
||||||
|
column: "_id",
|
||||||
|
component: RemoveUserTableRenderer,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const customAppTableRenderers = [
|
||||||
|
{
|
||||||
|
column: "name",
|
||||||
|
component: AppNameTableRenderer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: "role",
|
||||||
|
component: AppRoleTableRenderer,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
let popoverAnchor
|
let popoverAnchor
|
||||||
let popover
|
let popover
|
||||||
let searchTerm = ""
|
let searchTerm = ""
|
||||||
let prevSearch = undefined
|
let prevSearch = undefined
|
||||||
let pageInfo = createPaginationStore()
|
let pageInfo = createPaginationStore()
|
||||||
let loaded = false
|
let loaded = false
|
||||||
let editModal, deleteModal, appAddModal
|
let editModal, deleteModal
|
||||||
|
|
||||||
$: page = $pageInfo.page
|
$: page = $pageInfo.page
|
||||||
$: fetchUsers(page, searchTerm)
|
$: fetchUsers(page, searchTerm)
|
||||||
$: group = $groups.find(x => x._id === groupId)
|
$: group = $groups.find(x => x._id === groupId)
|
||||||
$: filtered = $users.data
|
$: filtered = $users.data
|
||||||
$: groupApps = $apps.filter(app =>
|
$: groupApps = $apps
|
||||||
groups.actions.getGroupAppIds(group).includes(apps.getProdAppID(app.devId))
|
.filter(app =>
|
||||||
|
groups.actions
|
||||||
|
.getGroupAppIds(group)
|
||||||
|
.includes(apps.getProdAppID(app.devId))
|
||||||
)
|
)
|
||||||
|
.map(app => ({
|
||||||
|
...app,
|
||||||
|
role: group?.roles?.[apps.getProdAppID(app.devId)],
|
||||||
|
}))
|
||||||
|
$: console.log(groupApps)
|
||||||
$: {
|
$: {
|
||||||
if (loaded && !group?._id) {
|
if (loaded && !group?._id) {
|
||||||
$goto("./")
|
$goto("./")
|
||||||
|
@ -93,6 +134,22 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const removeUser = async id => {
|
||||||
|
await groups.actions.removeUser(groupId, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeApp = async app => {
|
||||||
|
await groups.actions.removeApp(groupId, apps.getProdAppID(app.devId))
|
||||||
|
}
|
||||||
|
|
||||||
|
setContext("users", {
|
||||||
|
removeUser,
|
||||||
|
})
|
||||||
|
setContext("roles", {
|
||||||
|
updateRole: () => {},
|
||||||
|
removeRole: removeApp,
|
||||||
|
})
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
try {
|
try {
|
||||||
await Promise.all([groups.actions.init(), apps.load(), roles.fetch()])
|
await Promise.all([groups.actions.init(), apps.load(), roles.fetch()])
|
||||||
|
@ -143,76 +200,35 @@
|
||||||
/>
|
/>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
<List>
|
|
||||||
{#if group?.users.length}
|
<Table
|
||||||
{#each group.users as user}
|
schema={userSchema}
|
||||||
<ListItem
|
data={group?.users}
|
||||||
title={user.email}
|
allowEditRows={false}
|
||||||
on:click={() => $goto(`../users/${user._id}`)}
|
customPlaceholder
|
||||||
hoverable
|
customRenderers={customUserTableRenderers}
|
||||||
|
on:click={e => $goto(`../users/${e.detail._id}`)}
|
||||||
>
|
>
|
||||||
<Icon
|
<div class="placeholder" slot="placeholder">
|
||||||
on:click={e => {
|
<Heading size="S">This user group doesn't have any users</Heading>
|
||||||
groups.actions.removeUser(groupId, user._id)
|
</div>
|
||||||
e.stopPropagation()
|
</Table>
|
||||||
}}
|
|
||||||
hoverable
|
|
||||||
size="S"
|
|
||||||
name="Close"
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
{/each}
|
|
||||||
{:else}
|
|
||||||
<ListItem icon="UserGroup" title="This user group has no users" />
|
|
||||||
{/if}
|
|
||||||
</List>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<Layout noPadding gap="S">
|
<Layout noPadding gap="S">
|
||||||
<div class="header">
|
|
||||||
<Heading size="S">Apps</Heading>
|
<Heading size="S">Apps</Heading>
|
||||||
<div>
|
<Table
|
||||||
<Button on:click={appAddModal.show()} secondary>Add app</Button>
|
schema={appSchema}
|
||||||
</div>
|
data={groupApps}
|
||||||
</div>
|
customPlaceholder
|
||||||
<List>
|
allowEditRows={false}
|
||||||
{#if groupApps.length}
|
customRenderers={customAppTableRenderers}
|
||||||
{#each groupApps as app}
|
on:click={e => $goto(`../../overview/${e.detail.devId}`)}
|
||||||
<ListItem
|
|
||||||
title={app.name}
|
|
||||||
icon={app?.icon?.name || "Apps"}
|
|
||||||
iconColor={app?.icon?.color || ""}
|
|
||||||
on:click={() => $goto(`../../overview/${app.devId}`)}
|
|
||||||
hoverable
|
|
||||||
>
|
>
|
||||||
<div class="title ">
|
<div class="placeholder" slot="placeholder">
|
||||||
<StatusLight
|
<Heading size="S">This group doesn't have access to any apps</Heading>
|
||||||
square
|
|
||||||
color={RoleUtils.getRoleColour(
|
|
||||||
group.roles[apps.getProdAppID(app.devId)]
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{getRoleLabel(app.devId)}
|
|
||||||
</StatusLight>
|
|
||||||
</div>
|
</div>
|
||||||
<Icon
|
</Table>
|
||||||
on:click={e => {
|
|
||||||
groups.actions.removeApp(
|
|
||||||
groupId,
|
|
||||||
apps.getProdAppID(app.devId)
|
|
||||||
)
|
|
||||||
e.stopPropagation()
|
|
||||||
}}
|
|
||||||
hoverable
|
|
||||||
size="S"
|
|
||||||
name="Close"
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
{/each}
|
|
||||||
{:else}
|
|
||||||
<ListItem icon="Apps" title="This user group has access to no apps" />
|
|
||||||
{/if}
|
|
||||||
</List>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
</Layout>
|
</Layout>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -221,10 +237,6 @@
|
||||||
<CreateEditGroupModal {group} {saveGroup} />
|
<CreateEditGroupModal {group} {saveGroup} />
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal bind:this={appAddModal}>
|
|
||||||
<AppAddModal {group} />
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
bind:this={deleteModal}
|
bind:this={deleteModal}
|
||||||
title="Delete user group"
|
title="Delete user group"
|
||||||
|
@ -245,4 +257,8 @@
|
||||||
.header :global(.spectrum-Heading) {
|
.header :global(.spectrum-Heading) {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
.placeholder {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
<ModalContent
|
<ModalContent
|
||||||
onConfirm={() => saveGroup(group)}
|
onConfirm={() => saveGroup(group)}
|
||||||
size="M"
|
size="M"
|
||||||
title="Create User Group"
|
title={group?._rev ? "Edit group" : "Create group"}
|
||||||
confirmText="Save"
|
confirmText="Save"
|
||||||
>
|
>
|
||||||
<Input bind:value={group.name} label="Team name" />
|
<Input bind:value={group.name} label="Name" />
|
||||||
<div class="modal-format">
|
<div class="modal-format">
|
||||||
<div class="modal-inner">
|
<div class="modal-inner">
|
||||||
<Body size="XS">Icon</Body>
|
<Body size="XS">Icon</Body>
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script>
|
||||||
|
import { ActionButton } from "@budibase/bbui"
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
|
export let value
|
||||||
|
|
||||||
|
const userContext = getContext("users")
|
||||||
|
|
||||||
|
const onClick = e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
userContext.removeUser(value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ActionButton size="S" on:click={onClick}>Remove</ActionButton>
|
Loading…
Reference in New Issue