overview card updates
This commit is contained in:
parent
4543b1213f
commit
71179b0f1e
|
@ -148,28 +148,6 @@ export function getTemplateParams(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a new user group ID
|
|
||||||
* @returns {string} The new user group ID which info can be stored under.
|
|
||||||
*/
|
|
||||||
exports.generateUserGroupID = () => {
|
|
||||||
return `${DocumentTypes.GROUP}${SEPARATOR}${newid()}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets parameters for retrieving groups.
|
|
||||||
*/
|
|
||||||
exports.getUserGroupsParams = (groupId: any, otherProps = {}) => {
|
|
||||||
if (!groupId) {
|
|
||||||
groupId = ""
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...otherProps,
|
|
||||||
startkey: `${DocumentTypes.GROUP}${SEPARATOR}${groupId}`,
|
|
||||||
endkey: `${DocumentTypes.GROUP}${SEPARATOR}${groupId}${UNICODE_MAX}`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a new role ID.
|
* Generates a new role ID.
|
||||||
* @returns {string} The new role ID which the role doc can be stored under.
|
* @returns {string} The new role ID which the role doc can be stored under.
|
||||||
|
|
|
@ -23,10 +23,13 @@
|
||||||
let assignmentModal
|
let assignmentModal
|
||||||
let appGroups = []
|
let appGroups = []
|
||||||
let appUsers = []
|
let appUsers = []
|
||||||
|
let prevSearch = undefined,
|
||||||
|
search = undefined
|
||||||
let pageInfo = createPaginationStore()
|
let pageInfo = createPaginationStore()
|
||||||
|
|
||||||
$: page = $pageInfo.page
|
$: page = $pageInfo.page
|
||||||
$: fetchUsers(page)
|
$: console.log(page)
|
||||||
|
$: fetchUsers(page, search)
|
||||||
|
|
||||||
$: isProPlan = $auth.user?.license.plan.type === Constants.PlanType.FREE
|
$: isProPlan = $auth.user?.license.plan.type === Constants.PlanType.FREE
|
||||||
|
|
||||||
|
@ -43,19 +46,6 @@
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
$: filteredUsers =
|
|
||||||
$users.data?.filter(x => {
|
|
||||||
return !Object.keys(x.roles).find(y => {
|
|
||||||
return extractAppId(y) === extractAppId(app.appId)
|
|
||||||
})
|
|
||||||
}) || []
|
|
||||||
|
|
||||||
$: filteredGroups = $groups.filter(element => {
|
|
||||||
return !element.apps.find(y => {
|
|
||||||
return y.appId === app.appId
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
function extractAppId(id) {
|
function extractAppId(id) {
|
||||||
const split = id?.split("_") || []
|
const split = id?.split("_") || []
|
||||||
return split.length ? split[split.length - 1] : null
|
return split.length ? split[split.length - 1] : null
|
||||||
|
@ -99,10 +89,16 @@
|
||||||
groups.actions.save(group)
|
groups.actions.save(group)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchUsers(page) {
|
async function fetchUsers(page, search) {
|
||||||
if ($pageInfo.loading) {
|
if ($pageInfo.loading) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// need to remove the page if they've started searching
|
||||||
|
if (search && !prevSearch) {
|
||||||
|
pageInfo.reset()
|
||||||
|
page = undefined
|
||||||
|
}
|
||||||
|
prevSearch = search
|
||||||
try {
|
try {
|
||||||
pageInfo.loading()
|
pageInfo.loading()
|
||||||
await users.search({ page, appId: app.appId })
|
await users.search({ page, appId: app.appId })
|
||||||
|
@ -136,7 +132,7 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if isProPlan}
|
{#if isProPlan && appGroups.length}
|
||||||
<List title="User Groups">
|
<List title="User Groups">
|
||||||
{#each appGroups as group}
|
{#each appGroups as group}
|
||||||
<ListItem
|
<ListItem
|
||||||
|
@ -154,6 +150,7 @@
|
||||||
{/each}
|
{/each}
|
||||||
</List>
|
</List>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if appUsers.length}
|
||||||
<List title="Users">
|
<List title="Users">
|
||||||
{#each appUsers as user}
|
{#each appUsers as user}
|
||||||
<ListItem title={user.email} avatar>
|
<ListItem title={user.email} avatar>
|
||||||
|
@ -179,6 +176,7 @@
|
||||||
goToNextPage={pageInfo.nextPage}
|
goToNextPage={pageInfo.nextPage}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="align">
|
<div class="align">
|
||||||
<Layout gap="S">
|
<Layout gap="S">
|
||||||
|
@ -200,11 +198,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Modal bind:this={assignmentModal}>
|
<Modal bind:this={assignmentModal}>
|
||||||
<AssignmentModal
|
<AssignmentModal {app} {appUsers} {addData} />
|
||||||
userData={filteredUsers.length ? filteredUsers : $users.data}
|
|
||||||
groups={isProPlan ? filteredGroups : []}
|
|
||||||
{addData}
|
|
||||||
/>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -1,16 +1,54 @@
|
||||||
<script>
|
<script>
|
||||||
import { ModalContent, PickerDropdown, ActionButton } from "@budibase/bbui"
|
import {
|
||||||
|
ModalContent,
|
||||||
|
PickerDropdown,
|
||||||
|
ActionButton,
|
||||||
|
notifications,
|
||||||
|
} from "@budibase/bbui"
|
||||||
import { roles } from "stores/backend"
|
import { roles } from "stores/backend"
|
||||||
|
import { groups, users } from "stores/portal"
|
||||||
import { RoleUtils } from "@budibase/frontend-core"
|
import { RoleUtils } from "@budibase/frontend-core"
|
||||||
|
import { createPaginationStore } from "helpers/pagination"
|
||||||
|
|
||||||
|
export let app
|
||||||
export let addData
|
export let addData
|
||||||
export let userData = []
|
export let appUsers = []
|
||||||
export let groups = []
|
|
||||||
|
let prevSearch = undefined,
|
||||||
|
search = undefined
|
||||||
|
let pageInfo = createPaginationStore()
|
||||||
|
|
||||||
|
$: page = $pageInfo.page
|
||||||
|
$: fetchUsers(page, search)
|
||||||
|
async function fetchUsers(page, search) {
|
||||||
|
if ($pageInfo.loading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// need to remove the page if they've started searching
|
||||||
|
if (search && !prevSearch) {
|
||||||
|
pageInfo.reset()
|
||||||
|
page = undefined
|
||||||
|
}
|
||||||
|
prevSearch = search
|
||||||
|
try {
|
||||||
|
pageInfo.loading()
|
||||||
|
await users.search({ page, search })
|
||||||
|
pageInfo.fetched($users.hasNextPage, $users.nextPage)
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error("Error getting user list")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: filteredGroups = $groups.filter(element => {
|
||||||
|
return !element.apps.find(y => {
|
||||||
|
return y.appId === app.appId
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
$: optionSections = {
|
$: optionSections = {
|
||||||
...(groups.length && {
|
...(filteredGroups.length && {
|
||||||
groups: {
|
groups: {
|
||||||
data: groups,
|
data: filteredGroups,
|
||||||
getLabel: group => group.name,
|
getLabel: group => group.name,
|
||||||
getValue: group => group._id,
|
getValue: group => group._id,
|
||||||
getIcon: group => group.icon,
|
getIcon: group => group.icon,
|
||||||
|
@ -18,7 +56,7 @@
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
users: {
|
users: {
|
||||||
data: userData,
|
data: $users.data.filter(u => !appUsers.find(x => x._id === u._id)),
|
||||||
getLabel: user => user.email,
|
getLabel: user => user.email,
|
||||||
getValue: user => user._id,
|
getValue: user => user._id,
|
||||||
getIcon: user => user.icon,
|
getIcon: user => user.icon,
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
<script>
|
<script>
|
||||||
import DashCard from "components/common/DashCard.svelte"
|
import DashCard from "components/common/DashCard.svelte"
|
||||||
import { AppStatus } from "constants"
|
import { AppStatus } from "constants"
|
||||||
import { Icon, Heading, Link, Avatar, Layout } from "@budibase/bbui"
|
import { Icon, Heading, Link, Avatar, Layout, Body } from "@budibase/bbui"
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
import clientPackage from "@budibase/client/package.json"
|
import clientPackage from "@budibase/client/package.json"
|
||||||
import { processStringSync } from "@budibase/string-templates"
|
import { processStringSync } from "@budibase/string-templates"
|
||||||
import { users, auth } from "stores/portal"
|
import { users, auth } from "stores/portal"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher, onMount } from "svelte"
|
||||||
|
|
||||||
export let app
|
export let app
|
||||||
export let deployments
|
export let deployments
|
||||||
export let navigateTab
|
export let navigateTab
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
const unpublishApp = () => {
|
const unpublishApp = () => {
|
||||||
|
@ -18,7 +19,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let appEditor, appEditorPromise
|
let appEditor, appEditorPromise
|
||||||
|
$: console.log($users.data)
|
||||||
$: updateAvailable = clientPackage.version !== $store.version
|
$: updateAvailable = clientPackage.version !== $store.version
|
||||||
$: isPublished = app && app?.status === AppStatus.DEPLOYED
|
$: isPublished = app && app?.status === AppStatus.DEPLOYED
|
||||||
$: appEditorId = !app?.updatedBy ? $auth.user._id : app?.updatedBy
|
$: appEditorId = !app?.updatedBy ? $auth.user._id : app?.updatedBy
|
||||||
|
@ -37,6 +38,10 @@
|
||||||
|
|
||||||
return initials == "" ? user.email[0] : initials
|
return initials == "" ? user.email[0] : initials
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
await users.search({ page: undefined, appId: app.appId })
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="overview-tab">
|
<div class="overview-tab">
|
||||||
|
@ -132,6 +137,37 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</DashCard>
|
</DashCard>
|
||||||
|
<DashCard
|
||||||
|
title={"Access"}
|
||||||
|
showIcon={true}
|
||||||
|
action={() => {
|
||||||
|
navigateTab("Access")
|
||||||
|
}}
|
||||||
|
dataCy={"access"}
|
||||||
|
>
|
||||||
|
<div class="last-edited-content">
|
||||||
|
{#if $users?.data?.length}
|
||||||
|
<Layout noPadding gap="S">
|
||||||
|
<div class="users-tab">
|
||||||
|
{#each $users?.data as user}
|
||||||
|
<Avatar size="M" initials={getInitials(user)} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="users-text">
|
||||||
|
{$users?.data.length} users have access to this app
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
{:else}
|
||||||
|
<Layout noPadding gap="S">
|
||||||
|
<Body>No users</Body>
|
||||||
|
<div class="users-text">
|
||||||
|
No users have been assigned to this app
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</DashCard>
|
||||||
</div>
|
</div>
|
||||||
{#if false}
|
{#if false}
|
||||||
<div class="bottom">
|
<div class="bottom">
|
||||||
|
@ -186,6 +222,14 @@
|
||||||
grid-template-columns: repeat(auto-fill, minmax(30%, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(30%, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.users-tab {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
|
||||||
|
.users-text {
|
||||||
|
color: var(--spectrum-global-color-gray-600);
|
||||||
|
}
|
||||||
.overview-tab .bottom,
|
.overview-tab .bottom,
|
||||||
.automation-metrics {
|
.automation-metrics {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
const {
|
|
||||||
generateUserGroupID,
|
|
||||||
getUserGroupsParams,
|
|
||||||
} = require("@budibase/backend-core/db")
|
|
||||||
const { Configs } = require("../../../constants")
|
const { Configs } = require("../../../constants")
|
||||||
const email = require("../../../utilities/email")
|
const email = require("../../../utilities/email")
|
||||||
const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
|
const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
|
||||||
|
@ -11,13 +7,14 @@ const {
|
||||||
CacheKeys,
|
CacheKeys,
|
||||||
bustCache,
|
bustCache,
|
||||||
} = require("@budibase/backend-core/cache")
|
} = require("@budibase/backend-core/cache")
|
||||||
|
const { groups } = require("@budibase/pro")
|
||||||
|
|
||||||
exports.save = async function (ctx: any) {
|
exports.save = async function (ctx: any) {
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
|
|
||||||
// Config does not exist yet
|
// Config does not exist yet
|
||||||
if (!ctx.request.body._id) {
|
if (!ctx.request.body._id) {
|
||||||
ctx.request.body._id = generateUserGroupID(ctx.request.body.name)
|
ctx.request.body._id = groups.generateUserGroupID(ctx.request.body.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -34,7 +31,7 @@ exports.save = async function (ctx: any) {
|
||||||
exports.fetch = async function (ctx: any) {
|
exports.fetch = async function (ctx: any) {
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
const response = await db.allDocs(
|
const response = await db.allDocs(
|
||||||
getUserGroupsParams(null, {
|
groups.getUserGroupsParams(null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue