Update user and groups pages
This commit is contained in:
parent
0cdc814bcc
commit
16bfe97015
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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" },
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
<div style="float: right;">
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
|
@ -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}>
|
|
@ -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">
|
Loading…
Reference in New Issue