Update user and group details pages with new tables

This commit is contained in:
Andrew Kingston 2022-12-21 16:33:25 +00:00
parent e91ed89d72
commit 3dd5916f24
8 changed files with 155 additions and 131 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 () => {

View File

@ -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>

View File

@ -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 >
on:click={e => { <div class="placeholder" slot="placeholder">
groups.actions.removeUser(groupId, user._id) <Heading size="S">This user group doesn't have any users</Heading>
e.stopPropagation() </div>
}} </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> <Table
<div> schema={appSchema}
<Button on:click={appAddModal.show()} secondary>Add app</Button> data={groupApps}
customPlaceholder
allowEditRows={false}
customRenderers={customAppTableRenderers}
on:click={e => $goto(`../../overview/${e.detail.devId}`)}
>
<div class="placeholder" slot="placeholder">
<Heading size="S">This group doesn't have access to any apps</Heading>
</div> </div>
</div> </Table>
<List>
{#if groupApps.length}
{#each groupApps as app}
<ListItem
title={app.name}
icon={app?.icon?.name || "Apps"}
iconColor={app?.icon?.color || ""}
on:click={() => $goto(`../../overview/${app.devId}`)}
hoverable
>
<div class="title ">
<StatusLight
square
color={RoleUtils.getRoleColour(
group.roles[apps.getProdAppID(app.devId)]
)}
>
{getRoleLabel(app.devId)}
</StatusLight>
</div>
<Icon
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>

View File

@ -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>

View File

@ -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>