updated store and finished groups tab
This commit is contained in:
parent
b7cc71a623
commit
d952b9a3bb
|
@ -15,7 +15,7 @@ exports.DocumentTypes = {
|
||||||
ROLE: "role",
|
ROLE: "role",
|
||||||
MIGRATIONS: "migrations",
|
MIGRATIONS: "migrations",
|
||||||
DEV_INFO: "devinfo",
|
DEV_INFO: "devinfo",
|
||||||
GROUP: "gr"
|
GROUP: "gr",
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.StaticDatabases = {
|
exports.StaticDatabases = {
|
||||||
|
|
|
@ -170,7 +170,6 @@ exports.getUserGroupsParams = (groupId, otherProps = {}) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import clickOutside from "../Actions/click_outside"
|
import clickOutside from "../Actions/click_outside"
|
||||||
import { fly } from "svelte/transition"
|
import { fly } from "svelte/transition"
|
||||||
import Icon from "../Icon/Icon.svelte"
|
import Icon from "../Icon/Icon.svelte"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
export let value = "Anchor"
|
export let value = "Anchor"
|
||||||
export let size = "M"
|
export let size = "M"
|
||||||
|
@ -11,7 +12,7 @@
|
||||||
|
|
||||||
let open = false
|
let open = false
|
||||||
|
|
||||||
// const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
const iconList = [
|
const iconList = [
|
||||||
{
|
{
|
||||||
|
@ -44,12 +45,11 @@
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
/*
|
|
||||||
const onChange = value => {
|
const onChange = value => {
|
||||||
dispatch("change", value)
|
dispatch("change", value)
|
||||||
open = false
|
open = false
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
@ -76,7 +76,7 @@
|
||||||
{#each icon.icons as icon}
|
{#each icon.icons as icon}
|
||||||
<div
|
<div
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
value = icon
|
onChange(icon)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon name={icon} />
|
<Icon name={icon} />
|
||||||
|
|
|
@ -11,50 +11,66 @@
|
||||||
Search,
|
Search,
|
||||||
Divider,
|
Divider,
|
||||||
Detail,
|
Detail,
|
||||||
|
notifications,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import UserRow from "./_components/UserRow.svelte"
|
import UserRow from "./_components/UserRow.svelte"
|
||||||
import { users } from "stores/portal"
|
import { users, apps, groups } from "stores/portal"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
|
import GroupAppsRow from "./_components/GroupAppsRow.svelte"
|
||||||
|
|
||||||
|
export let groupId
|
||||||
let popoverAnchor
|
let popoverAnchor
|
||||||
let popover
|
let popover
|
||||||
|
$: group = $groups.find(x => x._id === groupId)
|
||||||
let searchTerm = ""
|
let searchTerm = ""
|
||||||
let selectedUsers = []
|
let selectedUsers = []
|
||||||
$: filteredUsers = $users.filter(user =>
|
$: filteredUsers = $users.filter(
|
||||||
user?.email?.toLowerCase().includes(searchTerm.toLowerCase())
|
user =>
|
||||||
|
selectedUsers &&
|
||||||
|
user?.email?.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
)
|
)
|
||||||
|
let app_list = [
|
||||||
let group = {
|
|
||||||
_id: "gr_123456",
|
|
||||||
color: "green",
|
|
||||||
icon: "Anchor",
|
|
||||||
name: "Core Team",
|
|
||||||
userCount: 5,
|
|
||||||
appCount: 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
let groupUsers = [
|
|
||||||
{
|
{
|
||||||
email: "peter@budibase.com",
|
|
||||||
access: "Developer",
|
access: "Developer",
|
||||||
|
name: "test app",
|
||||||
|
icon: "Anchor",
|
||||||
|
color: "blue",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
/*
|
async function addAll() {
|
||||||
function getGroup() {
|
selectedUsers = [...selectedUsers, ...filteredUsers]
|
||||||
return
|
group.users = selectedUsers
|
||||||
|
await groups.actions.save(group)
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
function selectUser(id) {
|
async function selectUser(id) {
|
||||||
let user = selectedUsers.find(user_id => user_id === id)
|
let selectedUser = selectedUsers.find(user_id => user_id === id)
|
||||||
if (user) {
|
let enrichedUser = $users.find(user => user._id === id)
|
||||||
selectedUsers = selectedUsers.filter(id => id !== user)
|
if (selectedUser) {
|
||||||
|
selectedUsers = selectedUsers.filter(id => id !== selectedUser)
|
||||||
|
let newUsers = group.users.filter(user => user._id !== id)
|
||||||
|
group.users = newUsers
|
||||||
} else {
|
} else {
|
||||||
selectedUsers = [...selectedUsers, id]
|
selectedUsers = [...selectedUsers, id]
|
||||||
|
group.users.push(enrichedUser)
|
||||||
}
|
}
|
||||||
|
await groups.actions.save(group)
|
||||||
}
|
}
|
||||||
onMount(() => {
|
|
||||||
console.log($users)
|
async function removeUser(id) {
|
||||||
|
let newUsers = group.users.filter(user => user._id !== id)
|
||||||
|
group.users = newUsers
|
||||||
|
await groups.actions.save(group)
|
||||||
|
}
|
||||||
|
onMount(async () => {
|
||||||
|
try {
|
||||||
|
await groups.actions.init()
|
||||||
|
await users.init()
|
||||||
|
await apps.load()
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error("Error fetching User Group data")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -66,13 +82,13 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<div style="background: {group.color};" class="circle">
|
<div style="background: {group?.color};" class="circle">
|
||||||
<div>
|
<div>
|
||||||
<Icon size="M" name={group.icon} />
|
<Icon size="M" name={group?.icon} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-padding">
|
<div class="text-padding">
|
||||||
<Heading>{group.name}</Heading>
|
<Heading>{group?.name}</Heading>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div bind:this={popoverAnchor}>
|
<div bind:this={popoverAnchor}>
|
||||||
|
@ -90,7 +106,9 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<ActionButton emphasized size="S">Add all</ActionButton>
|
<ActionButton on:click={addAll} emphasized size="S"
|
||||||
|
>Add all</ActionButton
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Divider noMargin />
|
<Divider noMargin />
|
||||||
|
@ -121,10 +139,39 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="usersTable">
|
<div class="usersTable">
|
||||||
{#if groupUsers.length}
|
{#if group?.users.length}
|
||||||
{#each groupUsers as user}
|
{#each group.users as user}
|
||||||
<div>
|
<div>
|
||||||
<UserRow {user} />
|
<UserRow {removeUser} {user} />
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
{:else}
|
||||||
|
<div>
|
||||||
|
<div class="title header text-padding">
|
||||||
|
<Icon name="UserGroup" />
|
||||||
|
<div class="text-padding">
|
||||||
|
<Body size="S">You have no users in this team</Body>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style="flex-direction: column; margin-top: var(--spacing-m)"
|
||||||
|
class="title"
|
||||||
|
>
|
||||||
|
<Heading weight="light" size="XS">Apps</Heading>
|
||||||
|
<div style="margin-top: var(--spacing-xs)">
|
||||||
|
<Body size="S">Manage apps that this User group has been assigned to</Body
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="" class="usersTable">
|
||||||
|
{#if app_list.length}
|
||||||
|
{#each app_list as app}
|
||||||
|
<div>
|
||||||
|
<GroupAppsRow {app} />
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
{:else}
|
{:else}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
ColorPicker,
|
||||||
|
Body,
|
||||||
|
ModalContent,
|
||||||
|
Input,
|
||||||
|
IconPicker,
|
||||||
|
} from "@budibase/bbui"
|
||||||
|
|
||||||
|
export let group
|
||||||
|
export let saveGroup
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ModalContent
|
||||||
|
onConfirm={() => saveGroup(group)}
|
||||||
|
size="M"
|
||||||
|
title="Create User Group"
|
||||||
|
confirmText="Save"
|
||||||
|
>
|
||||||
|
<Input bind:value={group.name} label="Team name" />
|
||||||
|
<div class="modal-format">
|
||||||
|
<div class="modal-inner">
|
||||||
|
<Body size="XS">Icon</Body>
|
||||||
|
<div class="modal-spacing">
|
||||||
|
<IconPicker
|
||||||
|
on:change={e => (group.icon = e.detail)}
|
||||||
|
bind:value={group.icon}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-inner">
|
||||||
|
<Body size="XS">Color</Body>
|
||||||
|
<div class="modal-spacing">
|
||||||
|
<ColorPicker
|
||||||
|
bind:value={group.color}
|
||||||
|
on:change={e => (group.color = e.detail)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ModalContent>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.modal-format {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-spacing {
|
||||||
|
margin-left: var(--spacing-l);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,58 @@
|
||||||
|
<script>
|
||||||
|
import { Icon, Body, StatusLight } from "@budibase/bbui"
|
||||||
|
|
||||||
|
export let app
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="title">
|
||||||
|
<div class="name" style="display: flex; margin-left: var(--spacing-xl)">
|
||||||
|
<div class="app-icon" style="color: {app.icon?.color || ''}">
|
||||||
|
<Icon size="XL" name={app.icon?.name || "Apps"} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="desktop">
|
||||||
|
<div style="display: flex; align-items: center;">
|
||||||
|
<Body size="M">{app.name}</Body>
|
||||||
|
<div style="opacity: 0.5; margin: var(--spacing-xs) 0 0 var(--spacing-m)">
|
||||||
|
<Body size="XS">{app.access}</Body>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="desktop" />
|
||||||
|
<div
|
||||||
|
style="display: flex; align-items: baseline; margin-right: var(--spacing-xl);"
|
||||||
|
>
|
||||||
|
<StatusLight purple />
|
||||||
|
<Body size="XS">{app.access}</Body>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.name {
|
||||||
|
grid-gap: var(--spacing-xl);
|
||||||
|
grid-template-columns: 75px 75px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
text-decoration: none;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.name :global(.spectrum-Heading) {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
margin-left: calc(1.5 * var(--spacing-xl));
|
||||||
|
}
|
||||||
|
.title :global(h1:hover) {
|
||||||
|
color: var(--spectrum-global-color-blue-600);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 130ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.desktop {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,50 +1,87 @@
|
||||||
<script>
|
<script>
|
||||||
import { Button, Icon, Body } from "@budibase/bbui"
|
import {
|
||||||
|
Button,
|
||||||
|
Icon,
|
||||||
|
Body,
|
||||||
|
ActionMenu,
|
||||||
|
MenuItem,
|
||||||
|
Modal,
|
||||||
|
} from "@budibase/bbui"
|
||||||
import { goto } from "@roxi/routify"
|
import { goto } from "@roxi/routify"
|
||||||
|
import CreateEditGroupModal from "./CreateEditGroupModal.svelte"
|
||||||
|
|
||||||
export let group
|
export let group
|
||||||
|
export let deleteGroup
|
||||||
|
export let saveGroup
|
||||||
|
let modal
|
||||||
|
|
||||||
let { icon, color, name, userCount, appCount } = group
|
function editGroup() {
|
||||||
|
modal.show()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<div class="name" style="display: flex; margin-left: var(--spacing-xl)">
|
<div class="name" style="display: flex; margin-left: var(--spacing-xl)">
|
||||||
<div style="background: {color};" class="circle">
|
<div style="background: {group.color};" class="circle">
|
||||||
<div>
|
<div>
|
||||||
<Icon size="M" name={icon} />
|
<Icon size="M" name={group.icon} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="name" data-cy="app-name-link"><Body size="S">{name}</Body></div>
|
<div class="name" data-cy="app-name-link">
|
||||||
|
<Body size="S">{group.name}</Body>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="desktop tableElement">
|
<div class="desktop tableElement">
|
||||||
<Icon name="User" />
|
<Icon name="User" />
|
||||||
<div style="margin-left: var(--spacing-l">
|
<div style="margin-left: var(--spacing-l">
|
||||||
{parseInt(userCount)} user{parseInt(userCount) === 1 ? "" : "s"}
|
{parseInt(group.userCount) || 0} user{parseInt(group.userCount) === 1
|
||||||
|
? ""
|
||||||
|
: "s"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="desktop tableElement">
|
<div class="desktop tableElement">
|
||||||
<Icon name="WebPage" />
|
<Icon name="WebPage" />
|
||||||
|
|
||||||
<div style="margin-left: var(--spacing-l">
|
<div style="margin-left: var(--spacing-l)">
|
||||||
{parseInt(appCount)} app{parseInt(appCount) === 1 ? "" : "s"}
|
{parseInt(group.appCount) || 0} app{parseInt(group.appCount) === 1
|
||||||
|
? ""
|
||||||
|
: "s"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="app-row-actions">
|
<div class="group-row-actions">
|
||||||
<div>
|
<div>
|
||||||
<Button on:click={() => $goto(`./${group._id}`)} size="S" cta
|
<Button on:click={() => $goto(`./${group._id}`)} size="S" cta
|
||||||
>Manage</Button
|
>Manage</Button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<ActionMenu align="right">
|
||||||
|
<span slot="control">
|
||||||
|
<Icon hoverable name="More" />
|
||||||
|
</span>
|
||||||
|
<MenuItem on:click={() => deleteGroup(group)} icon="Delete"
|
||||||
|
>Delete</MenuItem
|
||||||
|
>
|
||||||
|
<MenuItem on:click={() => editGroup(group)} icon="Delete">Edit</MenuItem
|
||||||
|
>
|
||||||
|
</ActionMenu>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Modal bind:this={modal}>
|
||||||
|
<CreateEditGroupModal {group} {saveGroup} />
|
||||||
|
</Modal>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.app-row-actions {
|
.group-row-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
float: right;
|
float: right;
|
||||||
margin-right: var(--spacing-xl);
|
margin-right: var(--spacing-xl);
|
||||||
|
grid-template-columns: 75px 75px;
|
||||||
|
grid-gap: var(--spacing-xl);
|
||||||
}
|
}
|
||||||
.name {
|
.name {
|
||||||
grid-gap: var(--spacing-xl);
|
grid-gap: var(--spacing-xl);
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
import { Icon, Body, Avatar } from "@budibase/bbui"
|
import { Icon, Body, Avatar } from "@budibase/bbui"
|
||||||
|
|
||||||
export let user
|
export let user
|
||||||
|
export let removeUser
|
||||||
function removeFromGroup() {}
|
$: console.log(user)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="title">
|
<div class="title">
|
||||||
|
@ -22,7 +22,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="desktop" />
|
<div class="desktop" />
|
||||||
<div><Icon on:click={removeFromGroup} hoverable size="L" name="Close" /></div>
|
<div>
|
||||||
|
<Icon on:click={() => removeUser(user._id)} hoverable size="L" name="Close" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.name {
|
.name {
|
||||||
|
|
|
@ -2,35 +2,34 @@
|
||||||
import {
|
import {
|
||||||
Layout,
|
Layout,
|
||||||
Heading,
|
Heading,
|
||||||
ColorPicker,
|
|
||||||
Body,
|
Body,
|
||||||
Button,
|
Button,
|
||||||
Modal,
|
Modal,
|
||||||
ModalContent,
|
|
||||||
Input,
|
|
||||||
Tag,
|
Tag,
|
||||||
Tags,
|
Tags,
|
||||||
IconPicker,
|
notifications,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { API } from "api"
|
|
||||||
import { groups } from "stores/portal"
|
import { groups } from "stores/portal"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
|
import CreateEditGroupModal from "./_components/CreateEditGroupModal.svelte"
|
||||||
|
|
||||||
import UserGroupsRow from "./_components/UserGroupsRow.svelte"
|
import UserGroupsRow from "./_components/UserGroupsRow.svelte"
|
||||||
|
|
||||||
let modal
|
let modal
|
||||||
let selectedColor
|
let group = {}
|
||||||
let selectedIcon
|
|
||||||
let groupName
|
|
||||||
let proPlan = true
|
let proPlan = true
|
||||||
|
|
||||||
async function saveConfig() {
|
async function deleteGroup(group) {
|
||||||
try {
|
try {
|
||||||
API.saveGroup({
|
groups.actions.delete(group)
|
||||||
color: selectedColor,
|
} catch (error) {
|
||||||
icon: selectedIcon,
|
notifications.error(`Failed to delete group`)
|
||||||
name: groupName,
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
async function saveGroup(group) {
|
||||||
|
try {
|
||||||
|
await groups.actions.save(group)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error(`Failed to save group`)
|
notifications.error(`Failed to save group`)
|
||||||
}
|
}
|
||||||
|
@ -38,7 +37,7 @@
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
try {
|
try {
|
||||||
await groups.init()
|
await groups.actions.init()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error("Error getting User groups")
|
notifications.error("Error getting User groups")
|
||||||
}
|
}
|
||||||
|
@ -81,56 +80,17 @@
|
||||||
<div class="groupTable">
|
<div class="groupTable">
|
||||||
{#each $groups as group}
|
{#each $groups as group}
|
||||||
<div>
|
<div>
|
||||||
<UserGroupsRow {group} />
|
<UserGroupsRow {saveGroup} {deleteGroup} {group} />
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<Modal bind:this={modal}>
|
<Modal bind:this={modal}>
|
||||||
<ModalContent
|
<CreateEditGroupModal {group} {saveGroup} />
|
||||||
onConfirm={saveConfig}
|
|
||||||
size="M"
|
|
||||||
title="Create User Group"
|
|
||||||
confirmText="Save"
|
|
||||||
>
|
|
||||||
<Input bind:value={groupName} label="Team name" />
|
|
||||||
<div class="modal-format">
|
|
||||||
<div class="modal-inner">
|
|
||||||
<Body size="XS">Icon</Body>
|
|
||||||
<div class="modal-spacing">
|
|
||||||
<IconPicker bind:value={selectedIcon} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-inner">
|
|
||||||
<Body size="XS">Color</Body>
|
|
||||||
<div class="modal-spacing">
|
|
||||||
<ColorPicker
|
|
||||||
bind:value={selectedColor}
|
|
||||||
on:change={e => (selectedColor = e.detail)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.modal-format {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 40%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-inner {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-spacing {
|
|
||||||
margin-left: var(--spacing-l);
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-buttons {
|
.align-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
column-gap: var(--spacing-xl);
|
column-gap: var(--spacing-xl);
|
||||||
|
|
|
@ -1,24 +1,46 @@
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { update } from "lodash"
|
|
||||||
|
|
||||||
export function createGroupsStore() {
|
export function createGroupsStore() {
|
||||||
const { subscribe, set } = writable([])
|
const store = writable([])
|
||||||
|
|
||||||
async function init() {
|
const actions = {
|
||||||
const users = await API.getGroups()
|
init: async () => {
|
||||||
set(users)
|
const users = await API.getGroups()
|
||||||
}
|
store.set(users)
|
||||||
|
},
|
||||||
|
|
||||||
async function save(data) {
|
save: async group => {
|
||||||
await API.saveGroup(data)
|
const response = await API.saveGroup(group)
|
||||||
}
|
group._id = response._id
|
||||||
|
group._rev = response._rev
|
||||||
|
store.update(state => {
|
||||||
|
const currentIdx = state.findIndex(gr => gr._id === response._id)
|
||||||
|
if (currentIdx >= 0) {
|
||||||
|
state.splice(currentIdx, 1, group)
|
||||||
|
} else {
|
||||||
|
state.push(group)
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
return {
|
delete: async group => {
|
||||||
subscribe,
|
await API.deleteGroup({
|
||||||
init,
|
id: group._id,
|
||||||
save,
|
rev: group._rev,
|
||||||
}
|
})
|
||||||
|
store.update(state => {
|
||||||
|
state = state.filter(state => state._id !== group._id)
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe: store.subscribe,
|
||||||
|
actions,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const groups = createGroupsStore()
|
export const groups = createGroupsStore()
|
||||||
|
|
|
@ -7,4 +7,4 @@ export { auth } from "./auth"
|
||||||
export { oidc } from "./oidc"
|
export { oidc } from "./oidc"
|
||||||
export { templates } from "./templates"
|
export { templates } from "./templates"
|
||||||
export { licensing } from "./licensing"
|
export { licensing } from "./licensing"
|
||||||
export { groups } from "./groups"
|
export { groups } from "./groups"
|
||||||
|
|
|
@ -1,18 +1,40 @@
|
||||||
export const buildGroupsEndpoints = API => ({
|
export const buildGroupsEndpoints = API => ({
|
||||||
/**
|
/**
|
||||||
* Creates or updates a user in the current tenant.
|
* Creates a user group.
|
||||||
* @param user the new user to create
|
* @param user the new group to create
|
||||||
*/
|
*/
|
||||||
saveGroup: async group => {
|
saveGroup: async group => {
|
||||||
return await API.post({
|
return await API.post({
|
||||||
url: "/api/global/groups",
|
url: "/api/global/groups",
|
||||||
body: group,
|
body: group,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getGroups: async () => {
|
/**
|
||||||
return await API.get({
|
* Gets all of the user groups
|
||||||
url: "/api/global/groups",
|
*/
|
||||||
})
|
getGroups: async () => {
|
||||||
|
return await API.get({
|
||||||
|
url: "/api/global/groups",
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* Gets a group by ID
|
||||||
|
*/
|
||||||
|
getGroup: async id => {
|
||||||
|
return await API.get({
|
||||||
|
url: `/api/global/groups/${id}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a user group
|
||||||
|
* @param id the id of the config to delete
|
||||||
|
* @param rev the revision of the config to delete
|
||||||
|
*/
|
||||||
|
deleteGroup: async ({ id, rev }) => {
|
||||||
|
return await API.delete({
|
||||||
|
url: `/api/global/groups/${id}/${rev}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -236,6 +236,6 @@ export const createAPIClient = config => {
|
||||||
...buildViewEndpoints(API),
|
...buildViewEndpoints(API),
|
||||||
...buildSelfEndpoints(API),
|
...buildSelfEndpoints(API),
|
||||||
...buildLicensingEndpoints(API),
|
...buildLicensingEndpoints(API),
|
||||||
...buildGroupsEndpoints(API)
|
...buildGroupsEndpoints(API),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
const {
|
|
||||||
generateUserGroupID,
|
|
||||||
getUserGroupsParams
|
|
||||||
} = require("@budibase/backend-core/db")
|
|
||||||
const { Configs } = require("../../../constants")
|
|
||||||
const email = require("../../../utilities/email")
|
|
||||||
const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
|
|
||||||
const env = require("../../../environment")
|
|
||||||
const {
|
|
||||||
withCache,
|
|
||||||
CacheKeys,
|
|
||||||
bustCache,
|
|
||||||
} = require("@budibase/backend-core/cache")
|
|
||||||
|
|
||||||
|
|
||||||
exports.save = async function (ctx) {
|
|
||||||
const db = getGlobalDB()
|
|
||||||
|
|
||||||
// Config does not exist yet
|
|
||||||
if (!ctx.request.body._id) {
|
|
||||||
ctx.request.body._id = generateUserGroupID(ctx.request.body.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await db.put(ctx.request.body)
|
|
||||||
ctx.body = {
|
|
||||||
_id: response.id,
|
|
||||||
_rev: response.rev,
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
ctx.throw(400, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.fetch = async function (ctx) {
|
|
||||||
const db = getGlobalDB()
|
|
||||||
console.log('in here')
|
|
||||||
const response = await db.allDocs(
|
|
||||||
getUserGroupsParams(null, {
|
|
||||||
include_docs: true,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
ctx.body = response.rows.map(row => row.doc)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
exports.destroy = async function (ctx) {
|
|
||||||
const db = getGlobalDB()
|
|
||||||
const { id, rev } = ctx.params
|
|
||||||
|
|
||||||
try {
|
|
||||||
await db.remove(id, rev)
|
|
||||||
ctx.body = { message: "Group deleted successfully" }
|
|
||||||
} catch (err) {
|
|
||||||
ctx.throw(err.status, err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
const {
|
||||||
|
generateUserGroupID,
|
||||||
|
getUserGroupsParams,
|
||||||
|
} = require("@budibase/backend-core/db")
|
||||||
|
const { Configs } = require("../../../constants")
|
||||||
|
const email = require("../../../utilities/email")
|
||||||
|
const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
|
||||||
|
const env = require("../../../environment")
|
||||||
|
const {
|
||||||
|
withCache,
|
||||||
|
CacheKeys,
|
||||||
|
bustCache,
|
||||||
|
} = require("@budibase/backend-core/cache")
|
||||||
|
|
||||||
|
exports.save = async function (ctx: any) {
|
||||||
|
const db = getGlobalDB()
|
||||||
|
|
||||||
|
// Config does not exist yet
|
||||||
|
if (!ctx.request.body._id) {
|
||||||
|
ctx.request.body._id = generateUserGroupID(ctx.request.body.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await db.put(ctx.request.body)
|
||||||
|
ctx.body = {
|
||||||
|
_id: response.id,
|
||||||
|
_rev: response.rev,
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
ctx.throw(400, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.fetch = async function (ctx: any) {
|
||||||
|
const db = getGlobalDB()
|
||||||
|
const response = await db.allDocs(
|
||||||
|
getUserGroupsParams(null, {
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
ctx.body = response.rows.map((row: any) => row.doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.destroy = async function (ctx: any) {
|
||||||
|
const db = getGlobalDB()
|
||||||
|
const { id, rev } = ctx.params
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.remove(id, rev)
|
||||||
|
ctx.body = { message: "Group deleted successfully" }
|
||||||
|
} catch (err: any) {
|
||||||
|
ctx.throw(err.status, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a group by ID from the global database.
|
||||||
|
*/
|
||||||
|
exports.find = async function (ctx: any) {
|
||||||
|
const db = getGlobalDB()
|
||||||
|
try {
|
||||||
|
ctx.body = await db.get(ctx.params.id)
|
||||||
|
} catch (err: any) {
|
||||||
|
ctx.throw(err.status, err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,21 +7,26 @@ const Joi = require("joi")
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
function buildGroupSaveValidation() {
|
function buildGroupSaveValidation() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.body(Joi.object({
|
return joiValidator.body(Joi.object({
|
||||||
color: Joi.string().required(),
|
_id: Joi.string().optional(),
|
||||||
icon: Joi.string().required(),
|
_rev: Joi.string().optional(),
|
||||||
name: Joi.string().required()
|
color: Joi.string().required(),
|
||||||
}).required())
|
icon: Joi.string().required(),
|
||||||
|
name: Joi.string().required(),
|
||||||
|
users: Joi.array().optional()
|
||||||
|
}).required())
|
||||||
}
|
}
|
||||||
|
|
||||||
router.post(
|
router
|
||||||
|
.post(
|
||||||
"/api/global/groups",
|
"/api/global/groups",
|
||||||
adminOnly,
|
adminOnly,
|
||||||
buildGroupSaveValidation(),
|
buildGroupSaveValidation(),
|
||||||
controller.save
|
controller.save
|
||||||
)
|
)
|
||||||
.get("/api/global/groups", controller.fetch)
|
.get("/api/global/groups", controller.fetch)
|
||||||
|
.delete("/api/global/groups/:id/:rev", adminOnly, controller.destroy)
|
||||||
|
.get("/api/global/groups/:id", adminOnly, controller.find)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
@ -26,5 +26,5 @@ exports.routes = [
|
||||||
statusRoutes,
|
statusRoutes,
|
||||||
selfRoutes,
|
selfRoutes,
|
||||||
licenseRoutes,
|
licenseRoutes,
|
||||||
userGroupRoutes
|
userGroupRoutes,
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue