Update user and groups pages

This commit is contained in:
Andrew Kingston 2022-10-28 08:03:06 +01:00
parent 0cdc814bcc
commit 16bfe97015
32 changed files with 170 additions and 185 deletions

View File

@ -1,10 +1,10 @@
<script> <script>
export let wide = false export let wide = false
export let maxWidth = "1080px" export let narrow = false
export let noPadding = false export let noPadding = false
</script> </script>
<div style="--max-width: {maxWidth}" class:wide class:noPadding> <div class:wide class:noPadding class:narrow>
<slot /> <slot />
</div> </div>
@ -14,7 +14,7 @@
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
align-items: stretch; align-items: stretch;
max-width: var(--max-width); max-width: 1080px;
margin: 0 auto; margin: 0 auto;
flex: 1 1 auto; flex: 1 1 auto;
padding-bottom: 50px; padding-bottom: 50px;
@ -22,11 +22,10 @@
.wide { .wide {
max-width: none; max-width: none;
margin: 0;
} }
.noPadding { .narrow {
padding: 0px; max-width: 800px;
margin: 0px; margin: 0;
} }
</style> </style>

View File

@ -4,7 +4,7 @@
export let active = false export let active = false
</script> </script>
<a on:click {url} class:active> <a on:click href={url} class:active>
{text} {text}
</a> </a>

View File

@ -40,16 +40,8 @@
menu = menu.concat([ menu = menu.concat([
{ {
title: "Users", title: "Users",
href: "/builder/portal/manage/users", href: "/builder/portal/users/users",
heading: "Manage",
}, },
isEnabled(TENANT_FEATURE_FLAGS.USER_GROUPS)
? {
title: "User Groups",
href: "/builder/portal/manage/groups",
badge: "New",
}
: undefined,
{ title: "Auth", href: "/builder/portal/manage/auth" }, { title: "Auth", href: "/builder/portal/manage/auth" },
{ title: "Email", href: "/builder/portal/manage/email" }, { title: "Email", href: "/builder/portal/manage/email" },
{ {

View File

@ -1,3 +0,0 @@
<div style="float: right;">
<slot />
</div>

View File

@ -9,7 +9,7 @@
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { fetchData } from "@budibase/frontend-core" import { fetchData } from "@budibase/frontend-core"
import { API } from "api" import { API } from "api"
import GroupIcon from "../../manage/groups/_components/GroupIcon.svelte" import GroupIcon from "../../users/groups/_components/GroupIcon.svelte"
export let app export let app
export let deployments export let deployments

View File

@ -0,0 +1,30 @@
<script>
import { Page } from "@budibase/bbui"
import { SideNav, SideNavItem, Content } from "components/portal/page"
import { isEnabled, TENANT_FEATURE_FLAGS } from "helpers/featureFlags"
import { isActive, url } from "@roxi/routify"
$: narrow = !$isActive("./users/index") && !$isActive("./groups/index")
</script>
<Page {narrow}>
<Content>
<div slot="side-nav">
<SideNav title="Users">
<SideNavItem
text="Users"
url={$url("./users")}
active={$isActive("./users")}
/>
<!--{#if isEnabled(TENANT_FEATURE_FLAGS.USER_GROUPS)}-->
<SideNavItem
text="Groups"
url={$url("./groups")}
active={$isActive("./groups")}
/>
<!--{/if}-->
</SideNav>
</div>
<slot />
</Content>
</Page>

View File

@ -1,7 +1,6 @@
<script> <script>
import { goto } from "@roxi/routify" import { url, goto } from "@roxi/routify"
import { import {
ActionButton,
Button, Button,
Layout, Layout,
Heading, Heading,
@ -26,6 +25,7 @@
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 AppAddModal from "./_components/AppAddModal.svelte"
import { Breadcrumbs, Breadcrumb } from "components/portal/page"
export let groupId export let groupId
@ -105,87 +105,80 @@
{#if loaded} {#if loaded}
<Layout noPadding gap="XL"> <Layout noPadding gap="XL">
<div> <Breadcrumbs>
<ActionButton on:click={() => $goto("../groups")} icon="ArrowLeft"> <Breadcrumb url={$url("./")} text="Groups" />
Back <Breadcrumb text={group?.name} />
</ActionButton> </Breadcrumbs>
</div>
<Layout noPadding gap="M"> <div class="header">
<div class="header"> <div class="title">
<div class="title"> <GroupIcon {group} size="L" />
<GroupIcon {group} size="L" /> <div class="text-padding">
<div class="text-padding"> <Heading>{group?.name}</Heading>
<Heading>{group?.name}</Heading>
</div>
</div>
<div>
<ActionMenu align="right">
<span slot="control">
<Icon hoverable name="More" />
</span>
<MenuItem icon="Refresh" on:click={() => editModal.show()}>
Edit
</MenuItem>
<MenuItem icon="Delete" on:click={() => deleteModal.show()}>
Delete
</MenuItem>
</ActionMenu>
</div> </div>
</div> </div>
<div>
<ActionMenu align="right">
<span slot="control">
<Icon hoverable name="More" />
</span>
<MenuItem icon="Refresh" on:click={() => editModal.show()}>
Edit
</MenuItem>
<MenuItem icon="Delete" on:click={() => deleteModal.show()}>
Delete
</MenuItem>
</ActionMenu>
</div>
</div>
<Divider /> <Layout noPadding gap="S">
<div class="header">
<Layout noPadding gap="S"> <Heading size="S">Users</Heading>
<div class="header"> <div bind:this={popoverAnchor}>
<Heading size="S">Users</Heading> <Button on:click={popover.show()} cta>Add user</Button>
<div bind:this={popoverAnchor}>
<Button on:click={popover.show()} icon="UserAdd" cta>
Add user
</Button>
</div>
<Popover align="right" bind:this={popover} anchor={popoverAnchor}>
<UserGroupPicker
bind:searchTerm
labelKey="email"
selected={group.users?.map(user => user._id)}
list={$users.data}
on:select={e => groups.actions.addUser(groupId, e.detail)}
on:deselect={e => groups.actions.removeUser(groupId, e.detail)}
/>
</Popover>
</div> </div>
<List> <Popover align="right" bind:this={popover} anchor={popoverAnchor}>
{#if group?.users.length} <UserGroupPicker
{#each group.users as user} bind:searchTerm
<ListItem labelKey="email"
title={user.email} selected={group.users?.map(user => user._id)}
on:click={() => $goto(`../users/${user._id}`)} list={$users.data}
on:select={e => groups.actions.addUser(groupId, e.detail)}
on:deselect={e => groups.actions.removeUser(groupId, e.detail)}
/>
</Popover>
</div>
<List>
{#if group?.users.length}
{#each group.users as user}
<ListItem
title={user.email}
on:click={() => $goto(`../users/${user._id}`)}
hoverable
>
<Icon
on:click={e => {
groups.actions.removeUser(groupId, user._id)
e.stopPropagation()
}}
hoverable hoverable
> size="S"
<Icon name="Close"
on:click={e => { />
groups.actions.removeUser(groupId, user._id) </ListItem>
e.stopPropagation() {/each}
}} {:else}
hoverable <ListItem icon="UserGroup" title="This user group has no users" />
size="S" {/if}
name="Close" </List>
/>
</ListItem>
{/each}
{:else}
<ListItem icon="UserGroup" title="This user group has no users" />
{/if}
</List>
</Layout>
</Layout> </Layout>
<Layout noPadding gap="S"> <Layout noPadding gap="S">
<div class="header"> <div class="header">
<Heading size="S">Apps</Heading> <Heading size="S">Apps</Heading>
<div> <div>
<Button on:click={appAddModal.show()} icon="ExperienceAdd" cta> <Button on:click={appAddModal.show()} newStyles secondary>
Add app Add app
</Button> </Button>
</div> </div>

View File

@ -40,7 +40,7 @@
] ]
$: schema = { $: schema = {
name: {}, name: { displayName: "Group" },
users: { sortable: false }, users: { sortable: false },
roles: { sortable: false, displayName: "Apps" }, roles: { sortable: false, displayName: "Apps" },
} }
@ -91,7 +91,7 @@
<Layout noPadding gap="M"> <Layout noPadding gap="M">
<Layout gap="XS" noPadding> <Layout gap="XS" noPadding>
<div class="title"> <div class="title">
<Heading size="M">User groups</Heading> <Heading size="M">Groups</Heading>
{#if !$licensing.groupsEnabled} {#if !$licensing.groupsEnabled}
<Tags> <Tags>
<Tag icon="LockClosed">Pro plan</Tag> <Tag icon="LockClosed">Pro plan</Tag>
@ -99,7 +99,7 @@
{/if} {/if}
</div> </div>
<Body> <Body>
Easily assign and manage your users' access with user groups. Easily assign and manage your users' access with groups.
{#if !$auth.accountPortalAccess && !$licensing.groupsEnabled && $admin.cloud} {#if !$auth.accountPortalAccess && !$licensing.groupsEnabled && $admin.cloud}
Contact your account holder to upgrade your plan. Contact your account holder to upgrade your plan.
{/if} {/if}
@ -110,14 +110,7 @@
<ButtonGroup> <ButtonGroup>
{#if $licensing.groupsEnabled} {#if $licensing.groupsEnabled}
<!--Show the group create button--> <!--Show the group create button-->
<Button <Button newStyles cta on:click={showCreateGroupModal}>Add group</Button>
newStyles
icon={"UserGroup"}
cta
on:click={showCreateGroupModal}
>
Create user group
</Button>
{:else} {:else}
<Button <Button
newStyles newStyles

View File

@ -1,7 +1,6 @@
<script> <script>
import { goto } from "@roxi/routify" import { goto, url } from "@roxi/routify"
import { import {
ActionButton,
ActionMenu, ActionMenu,
Avatar, Avatar,
Button, Button,
@ -18,7 +17,6 @@
Select, Select,
Modal, Modal,
notifications, notifications,
Divider,
Banner, Banner,
StatusLight, StatusLight,
} from "@budibase/bbui" } from "@budibase/bbui"
@ -30,6 +28,7 @@
import DeleteUserModal from "./_components/DeleteUserModal.svelte" import DeleteUserModal from "./_components/DeleteUserModal.svelte"
import GroupIcon from "../groups/_components/GroupIcon.svelte" import GroupIcon from "../groups/_components/GroupIcon.svelte"
import { Constants, RoleUtils } from "@budibase/frontend-core" import { Constants, RoleUtils } from "@budibase/frontend-core"
import { Breadcrumbs, Breadcrumb } from "components/portal/page"
export let userId export let userId
@ -191,85 +190,76 @@
{#if loaded} {#if loaded}
<Layout gap="XL" noPadding> <Layout gap="XL" noPadding>
<div> <Breadcrumbs>
<ActionButton on:click={() => $goto("./")} icon="ArrowLeft"> <Breadcrumb url={$url("./")} text="Users" />
Back <Breadcrumb text={user?.email} />
</ActionButton> </Breadcrumbs>
</div>
<Layout noPadding gap="M"> <div class="title">
<div class="title"> <div>
<div> <div style="display: flex;">
<div style="display: flex;"> <Avatar size="XXL" {initials} />
<Avatar size="XXL" {initials} /> <div class="subtitle">
<div class="subtitle"> <Heading size="M">{nameLabel}</Heading>
<Heading size="S">{nameLabel}</Heading> {#if nameLabel !== user?.email}
{#if nameLabel !== user?.email} <Body size="S">{user?.email}</Body>
<Body size="S">{user?.email}</Body> {/if}
{/if}
</div>
</div> </div>
</div> </div>
{#if userId !== $auth.user?._id} </div>
<div> {#if userId !== $auth.user?._id}
<ActionMenu align="right"> <div>
<span slot="control"> <ActionMenu align="right">
<Icon hoverable name="More" /> <span slot="control">
</span> <Icon hoverable name="More" />
<MenuItem on:click={resetPasswordModal.show} icon="Refresh"> </span>
Force password reset <MenuItem on:click={resetPasswordModal.show} icon="Refresh">
</MenuItem> Force password reset
<MenuItem on:click={deleteModal.show} icon="Delete"> </MenuItem>
Delete <MenuItem on:click={deleteModal.show} icon="Delete">
</MenuItem> Delete
</ActionMenu> </MenuItem>
</ActionMenu>
</div>
{/if}
</div>
<Layout noPadding gap="S">
<Heading size="S">Details</Heading>
<div class="fields">
<div class="field">
<Label size="L">Email</Label>
<Input disabled value={user?.email} />
</div>
<div class="field">
<Label size="L">First name</Label>
<Input value={user?.firstName} on:blur={updateUserFirstName} />
</div>
<div class="field">
<Label size="L">Last name</Label>
<Input value={user?.lastName} on:blur={updateUserLastName} />
</div>
<!-- don't let a user remove the privileges that let them be here -->
{#if userId !== $auth.user._id}
<div class="field">
<Label size="L">Role</Label>
<Select
value={globalRole}
options={Constants.BudibaseRoleOptions}
on:change={updateUserRole}
/>
</div> </div>
{/if} {/if}
</div> </div>
<Divider />
<Layout noPadding gap="S">
<Heading size="S">Details</Heading>
<div class="fields">
<div class="field">
<Label size="L">Email</Label>
<Input disabled value={user?.email} />
</div>
<div class="field">
<Label size="L">First name</Label>
<Input value={user?.firstName} on:blur={updateUserFirstName} />
</div>
<div class="field">
<Label size="L">Last name</Label>
<Input value={user?.lastName} on:blur={updateUserLastName} />
</div>
<!-- don't let a user remove the privileges that let them be here -->
{#if userId !== $auth.user._id}
<div class="field">
<Label size="L">Role</Label>
<Select
value={globalRole}
options={Constants.BudibaseRoleOptions}
on:change={updateUserRole}
/>
</div>
{/if}
</div>
</Layout>
</Layout> </Layout>
{#if $licensing.groupsEnabled} {#if $licensing.groupsEnabled}
<!-- User groups --> <!-- User groups -->
<Layout gap="S" noPadding> <Layout gap="S" noPadding>
<div class="tableTitle"> <div class="tableTitle">
<Heading size="S">User groups</Heading> <Heading size="S">Groups</Heading>
<div bind:this={popoverAnchor}> <div bind:this={popoverAnchor}>
<Button <Button on:click={popover.show()} secondary newStyles>
on:click={popover.show()} Add to group
icon="UserGroup"
secondary
newStyles
>
Add to user group
</Button> </Button>
</div> </div>
<Popover align="right" bind:this={popover} anchor={popoverAnchor}> <Popover align="right" bind:this={popover} anchor={popoverAnchor}>

View File

@ -218,20 +218,11 @@
<Divider /> <Divider />
<div class="controls"> <div class="controls">
<ButtonGroup> <ButtonGroup>
<Button <Button dataCy="add-user" on:click={createUserModal.show} cta>
dataCy="add-user" Add users
on:click={createUserModal.show}
icon="UserAdd"
cta
>Add users
</Button> </Button>
<Button <Button on:click={importUsersModal.show} secondary newStyles>
on:click={importUsersModal.show} Import
icon="Import"
secondary
newStyles
>
Import users
</Button> </Button>
</ButtonGroup> </ButtonGroup>
<div class="controls-right"> <div class="controls-right">