add base audit logs ui
This commit is contained in:
parent
2bd6ff627a
commit
70ac7b81c3
|
@ -14,6 +14,7 @@
|
|||
export let autocomplete = false
|
||||
export let sort = false
|
||||
export let autoWidth = false
|
||||
export let fetchTerm = null
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
|
@ -83,6 +84,7 @@
|
|||
{options}
|
||||
isPlaceholder={!value?.length}
|
||||
{autocomplete}
|
||||
bind:fetchTerm
|
||||
{isOptionSelected}
|
||||
{getOptionLabel}
|
||||
{getOptionValue}
|
||||
|
|
|
@ -31,7 +31,8 @@
|
|||
export let autoWidth = false
|
||||
export let autocomplete = false
|
||||
export let sort = false
|
||||
|
||||
export let fetchTerm = null
|
||||
$: console.log(fieldText)
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let searchTerm = null
|
||||
|
@ -71,7 +72,7 @@
|
|||
}
|
||||
|
||||
const getFilteredOptions = (options, term, getLabel) => {
|
||||
if (autocomplete && term) {
|
||||
if (autocomplete && term && !fetchTerm) {
|
||||
const lowerCaseTerm = term.toLowerCase()
|
||||
return options.filter(option => {
|
||||
return `${getLabel(option)}`.toLowerCase().includes(lowerCaseTerm)
|
||||
|
@ -144,8 +145,8 @@
|
|||
>
|
||||
{#if autocomplete}
|
||||
<Search
|
||||
value={searchTerm}
|
||||
on:change={event => (searchTerm = event.detail)}
|
||||
value={fetchTerm ? fetchTerm : searchTerm}
|
||||
on:change={event => (fetchTerm = event.detail)}
|
||||
{disabled}
|
||||
placeholder="Search"
|
||||
/>
|
||||
|
@ -247,7 +248,7 @@
|
|||
}
|
||||
.popover-content.auto-width .spectrum-Menu-itemLabel {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
overflow: none;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.popover-content:not(.auto-width) .spectrum-Menu-itemLabel {
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
export let getOptionValue = option => option
|
||||
export let sort = false
|
||||
export let autoWidth = false
|
||||
export let autocomplete = false
|
||||
export let fetchTerm = null
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const onChange = e => {
|
||||
value = e.detail
|
||||
|
@ -34,6 +37,8 @@
|
|||
{getOptionLabel}
|
||||
{getOptionValue}
|
||||
{autoWidth}
|
||||
{autocomplete}
|
||||
bind:fetchTerm
|
||||
on:change={onChange}
|
||||
on:click
|
||||
/>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
export let autoWidth = false
|
||||
export let sort = false
|
||||
export let tooltip = ""
|
||||
export let autocomplete = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const onChange = e => {
|
||||
|
@ -51,6 +52,7 @@
|
|||
{getOptionIcon}
|
||||
{getOptionColour}
|
||||
{isOptionEnabled}
|
||||
{autocomplete}
|
||||
on:change={onChange}
|
||||
on:click
|
||||
/>
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
import { Content, SideNav, SideNavItem } from "components/portal/page"
|
||||
import { menu } from "stores/portal"
|
||||
|
||||
$: wide = $isActive("./auditLogs")
|
||||
$: pages = $menu.find(x => x.title === "Account")?.subPages || []
|
||||
$: !pages.length && $goto("../")
|
||||
</script>
|
||||
|
||||
<Page>
|
||||
<Content narrow>
|
||||
<Content narrow={!wide}>
|
||||
<div slot="side-nav">
|
||||
<SideNav>
|
||||
{#each pages as { title, href }}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<script>
|
||||
import { Avatar } from "@budibase/bbui"
|
||||
export let row
|
||||
</script>
|
||||
|
||||
<Avatar size="M" initials={"PC"} />
|
|
@ -0,0 +1,13 @@
|
|||
<script>
|
||||
import { ActionButton } from "@budibase/bbui"
|
||||
import { getContext } from "svelte"
|
||||
|
||||
export let row
|
||||
const auditLogs = getContext("auditLogs")
|
||||
const onClick = e => {
|
||||
e.stopPropagation()
|
||||
auditLogs.viewDetails(row)
|
||||
}
|
||||
</script>
|
||||
|
||||
<ActionButton size="S" on:click={onClick}>Edit</ActionButton>
|
|
@ -0,0 +1,200 @@
|
|||
<script>
|
||||
import {
|
||||
Layout,
|
||||
Table,
|
||||
Select,
|
||||
Search,
|
||||
Multiselect,
|
||||
notifications,
|
||||
} from "@budibase/bbui"
|
||||
import { licensing, users, apps } from "stores/portal"
|
||||
import LockedFeature from "../../_components/LockedFeature.svelte"
|
||||
import { createPaginationStore } from "helpers/pagination"
|
||||
import { getContext, setContext } from "svelte"
|
||||
import Portal from "svelte-portal"
|
||||
import ViewDetailsRenderer from "./_components/ViewDetailsRenderer.svelte"
|
||||
import UserRenderer from "./_components/UserRenderer.svelte"
|
||||
|
||||
const sidePanel = getContext("side-panel")
|
||||
const schema = {
|
||||
name: {},
|
||||
date: {},
|
||||
user: { width: "auto" },
|
||||
app: {},
|
||||
event: {},
|
||||
view: { width: "auto", borderLeft: true, displayName: "" },
|
||||
}
|
||||
|
||||
const customRenderers = [
|
||||
{
|
||||
column: "view",
|
||||
component: ViewDetailsRenderer,
|
||||
},
|
||||
{
|
||||
column: "user",
|
||||
component: UserRenderer,
|
||||
},
|
||||
]
|
||||
|
||||
let searchTerm = ""
|
||||
let pageInfo = createPaginationStore()
|
||||
let prevSearch = undefined
|
||||
let selectedUsers = []
|
||||
let selectedLog
|
||||
|
||||
let data = [
|
||||
{
|
||||
name: "User created",
|
||||
date: "2021-03-01 12:00:00",
|
||||
user: "Peter Clement",
|
||||
app: "School Admin Panel",
|
||||
event: "User added",
|
||||
metadata: {
|
||||
name: "Peter Clement",
|
||||
email: "",
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
$: fetchUsers(page, searchTerm)
|
||||
$: page = $pageInfo.page
|
||||
$: enrichedList = enrich($users.data || [], selectedUsers)
|
||||
$: sortedList = sort(enrichedList)
|
||||
|
||||
const fetchUsers = async (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, email: search })
|
||||
pageInfo.fetched($users.hasNextPage, $users.nextPage)
|
||||
} catch (error) {
|
||||
notifications.error("Error getting user list")
|
||||
}
|
||||
}
|
||||
|
||||
const enrich = (list, selected) => {
|
||||
return list.map(item => {
|
||||
return {
|
||||
...item,
|
||||
selected: selected.find(x => x === item._id) != null,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const sort = list => {
|
||||
let sortedList = list.slice()
|
||||
sortedList?.sort((a, b) => {
|
||||
if (a.selected === b.selected) {
|
||||
return a["email"] < b["email"] ? -1 : 1
|
||||
} else if (a.selected) {
|
||||
return -1
|
||||
} else if (b.selected) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
return sortedList
|
||||
}
|
||||
|
||||
const viewDetails = detail => {
|
||||
selectedLog = detail
|
||||
sidePanel.open()
|
||||
}
|
||||
|
||||
setContext("auditLogs", {
|
||||
viewDetails,
|
||||
})
|
||||
</script>
|
||||
|
||||
<LockedFeature
|
||||
title={"Audit Logs"}
|
||||
planType={"Business plan"}
|
||||
description={"View all events that have occurred in your Budibase installation"}
|
||||
enabled={$licensing.auditLogsEnabled}
|
||||
upgradeButtonClick={async () => {
|
||||
$licensing.goToUpgradePage()
|
||||
}}
|
||||
>
|
||||
<div class="controls">
|
||||
<div class="search">
|
||||
<div class="select">
|
||||
<Select placeholder="All" label="Activity" />
|
||||
</div>
|
||||
<div class="select">
|
||||
<Multiselect
|
||||
bind:fetchTerm={searchTerm}
|
||||
placeholder="All users"
|
||||
label="Users"
|
||||
autocomplete
|
||||
bind:value={selectedUsers}
|
||||
getOptionValue={user => user._id}
|
||||
getOptionLabel={user => user.email}
|
||||
options={sortedList}
|
||||
/>
|
||||
</div>
|
||||
<div class="select">
|
||||
<Multiselect
|
||||
placeholder="All apps"
|
||||
label="App"
|
||||
getOptionValue={app => app.appId}
|
||||
getOptionLabel={app => app.name}
|
||||
options={$apps}
|
||||
/>
|
||||
</div>
|
||||
<div class="select">
|
||||
<Multiselect placeholder="All events" label="Event" />
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 200px;">
|
||||
<Search placeholder="Search" value={""} />
|
||||
</div>
|
||||
</div>
|
||||
<Layout noPadding>
|
||||
<Table
|
||||
{customRenderers}
|
||||
on:click={viewDetails}
|
||||
{data}
|
||||
allowEditColumns={false}
|
||||
allowEditRows={false}
|
||||
allowSelectRows={false}
|
||||
{schema}
|
||||
/>
|
||||
</Layout>
|
||||
</LockedFeature>
|
||||
|
||||
{#if selectedLog}
|
||||
<Portal target="#side-panel">
|
||||
<div>hello</div>
|
||||
</Portal>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--spacing-l);
|
||||
align-items: flex-end;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.search {
|
||||
display: flex;
|
||||
gap: var(--spacing-m);
|
||||
align-items: flex-start;
|
||||
flex: 1 1 auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
.select {
|
||||
flex: 1 1 0;
|
||||
max-width: 200px;
|
||||
min-width: 80px;
|
||||
}
|
||||
</style>
|
|
@ -184,7 +184,7 @@
|
|||
<Divider />
|
||||
|
||||
{#if !$licensing.backupsEnabled}
|
||||
{#if !$auth.accountPortalAccess && !$licensing.groupsEnabled && $admin.cloud}
|
||||
{#if !$auth.accountPortalAccess && $admin.cloud}
|
||||
<Body>Contact your account holder to upgrade your plan.</Body>
|
||||
{/if}
|
||||
<div class="pro-buttons">
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
notifications.error(`Error saving variable: ${err.message}`)
|
||||
}
|
||||
}
|
||||
$: console.log($environment.variables)
|
||||
</script>
|
||||
|
||||
<LockedFeature
|
||||
|
|
|
@ -64,6 +64,10 @@ export const createLicensingStore = () => {
|
|||
Constants.Features.ENVIRONMENT_VARIABLES
|
||||
)
|
||||
|
||||
let auditLogsEnabled = license.features.includes(
|
||||
Constants.Features.AUDIT_LOGS
|
||||
)
|
||||
auditLogsEnabled = true
|
||||
store.update(state => {
|
||||
return {
|
||||
...state,
|
||||
|
@ -72,6 +76,7 @@ export const createLicensingStore = () => {
|
|||
groupsEnabled,
|
||||
backupsEnabled,
|
||||
environmentVariablesEnabled,
|
||||
auditLogsEnabled,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
@ -75,6 +75,10 @@ export const menu = derived([admin, auth], ([$admin, $auth]) => {
|
|||
title: "Usage",
|
||||
href: "/builder/portal/account/usage",
|
||||
},
|
||||
{
|
||||
title: "Audit Logs",
|
||||
href: "/builder/portal/account/auditLogs",
|
||||
},
|
||||
]
|
||||
if ($admin.cloud && $auth?.user?.accountPortalAccess) {
|
||||
accountSubPages.push({
|
||||
|
@ -87,6 +91,7 @@ export const menu = derived([admin, auth], ([$admin, $auth]) => {
|
|||
href: "/builder/portal/account/upgrade",
|
||||
})
|
||||
}
|
||||
// add license check here
|
||||
if (
|
||||
$auth?.user?.accountPortalAccess &&
|
||||
$auth.user.account.stripeCustomerId
|
||||
|
|
|
@ -115,6 +115,7 @@ export const Features = {
|
|||
USER_GROUPS: "userGroups",
|
||||
BACKUPS: "appBackups",
|
||||
ENVIRONMENT_VARIABLES: "environmentVariables",
|
||||
AUDIT_LOGS: "auditLogs",
|
||||
}
|
||||
|
||||
// Role IDs
|
||||
|
|
Loading…
Reference in New Issue