Add role setting to navigation links to allow easily customising which roles see which links

This commit is contained in:
Andrew Kingston 2022-06-09 14:28:02 +01:00
parent 5a35c182c1
commit 3d1c5111e9
7 changed files with 63 additions and 31 deletions

View File

@ -11,6 +11,7 @@
import { dndzone } from "svelte-dnd-action"
import { generate } from "shortid"
import { store } from "builderStore"
import RoleSelect from "components/design/settings/controls/RoleSelect.svelte"
export let links = []
@ -75,6 +76,7 @@
placeholder="URL"
options={urlOptions}
/>
<RoleSelect bind:value={link.roleId} placeholder="Minimum role" />
<Icon
name="Close"
hoverable
@ -95,7 +97,7 @@
<style>
.container {
width: 100%;
max-width: 600px;
max-width: 800px;
margin: 0 auto;
}
.links {

View File

@ -59,6 +59,7 @@
$screenStore.screens,
$devToolsStore.role
)
permissionError = false
routeStore.actions.navigate(route)
} else {
// No screens likely means the user has no permissions to view this app
@ -120,17 +121,24 @@
dir="ltr"
class="spectrum spectrum--medium {$themeStore.theme}"
>
{#if permissionError}
<div class="error">
<Layout justifyItems="center" gap="S">
{@html ErrorSVG}
<Heading size="L">You don't have permission to use this app</Heading>
<Body size="S">Ask your administrator to grant you access</Body>
</Layout>
</div>
{:else if $screenStore.activeLayout}
<DeviceBindingsProvider>
<UserBindingsProvider>
<DeviceBindingsProvider>
{#if permissionError}
<div class="error-wrapper">
{#if isDevPreview}
<DevToolsHeader />
{/if}
<div class="error">
<Layout justifyItems="center" gap="S">
{@html ErrorSVG}
<Heading size="L"
>You don't have permission to use this app</Heading
>
<Body size="S">Ask your administrator to grant you access</Body>
</Layout>
</div>
</div>
{:else if $screenStore.activeLayout}
<StateBindingsProvider>
<RowSelectionProvider>
<!-- Settings bar can be rendered outside of device preview -->
@ -198,9 +206,9 @@
</div>
</RowSelectionProvider>
</StateBindingsProvider>
</DeviceBindingsProvider>
{/if}
</UserBindingsProvider>
{/if}
</DeviceBindingsProvider>
</div>
<KeyboardManager />
{/if}
@ -248,8 +256,11 @@
overflow: hidden;
}
.error-wrapper {
width: 100%;
height: 100%;
}
.error {
position: absolute;
width: 100%;
height: 100%;
display: grid;
@ -258,23 +269,19 @@
text-align: center;
padding: 20px;
}
.error :global(svg) {
fill: var(--spectrum-global-color-gray-500);
width: 80px;
height: 80px;
}
.error :global(h1),
.error :global(p) {
color: var(--spectrum-global-color-gray-800);
}
.error :global(p) {
font-style: italic;
margin-top: -0.5em;
}
.error :global(h1) {
font-weight: 400;
}
@ -284,12 +291,10 @@
#clip-root.preview {
padding: 2px;
}
#clip-root.tablet-preview {
width: calc(1024px + 6px);
height: calc(768px + 6px);
}
#clip-root.mobile-preview {
width: calc(390px + 6px);
height: calc(844px + 6px);

View File

@ -2,10 +2,12 @@
import { getContext, setContext } from "svelte"
import { writable } from "svelte/store"
import { Heading, Icon } from "@budibase/bbui"
import { FieldTypes } from "../../constants"
import { FieldTypes } from "constants"
import active from "svelte-spa-router/active"
import { RoleUtils } from "@budibase/frontend-core"
const { routeStore, styleable, linkable, builderStore } = getContext("sdk")
const sdk = getContext("sdk")
const { routeStore, styleable, linkable, builderStore, currentRole } = sdk
const component = getContext("component")
const context = getContext("context")
@ -50,7 +52,7 @@
})
setContext("layout", store)
$: validLinks = links?.filter(link => link.text && link.url) || []
$: validLinks = getValidLinks(links, $currentRole)
$: typeClass = NavigationClasses[navigation] || NavigationClasses.None
$: navWidthClass = WidthClasses[navWidth || width] || WidthClasses.Large
$: pageWidthClass = WidthClasses[pageWidth || width] || WidthClasses.Large
@ -79,6 +81,17 @@
}
}
const getValidLinks = (allLinks, role) => {
// Strip links missing required info
let validLinks = (allLinks || []).filter(link => link.text && link.url)
// Filter to only links allowed by the current role
const priority = RoleUtils.getRolePriority(role)
return validLinks.filter(link => {
return !link.roleId || RoleUtils.getRolePriority(link.roleId) <= priority
})
}
const isInternal = url => {
return url.startsWith("/")
}

View File

@ -21,11 +21,11 @@
}
onMount(() => {
resizeObserver.observe(document.getElementById("app-root"))
resizeObserver.observe(document.getElementById("spectrum-root"))
})
onDestroy(() => {
resizeObserver.unobserve(document.getElementById("app-root"))
resizeObserver.unobserve(document.getElementById("spectrum-root"))
})
</script>

View File

@ -1,6 +1,6 @@
<script>
import Provider from "./Provider.svelte"
import { authStore, devToolsStore } from "stores"
import { authStore, currentRole } from "stores"
import { ActionTypes } from "constants"
import { Constants } from "@budibase/frontend-core"
@ -17,10 +17,6 @@
]
</script>
<Provider
key="user"
data={{ ...$authStore, roleId: $devToolsStore.role || $authStore?.roleId }}
{actions}
>
<Provider key="user" data={{ ...$authStore, roleId: $currentRole }} {actions}>
<slot />
</Provider>

View File

@ -8,6 +8,7 @@ import {
uploadStore,
rowSelectionStore,
componentStore,
currentRole,
} from "stores"
import { styleable } from "utils/styleable"
import { linkable } from "utils/linkable"
@ -26,6 +27,7 @@ export default {
builderStore,
uploadStore,
componentStore,
currentRole,
styleable,
linkable,
getAction,

View File

@ -1,3 +1,7 @@
import { derived } from "svelte/store"
import { devToolsStore } from "./devTools.js"
import { authStore } from "./auth.js"
export { authStore } from "./auth"
export { appStore } from "./app"
export { notificationStore } from "./notification"
@ -13,8 +17,18 @@ export { devToolsStore } from "./devTools"
export { componentStore } from "./components"
export { uploadStore } from "./uploads.js"
export { rowSelectionStore } from "./rowSelection.js"
// Context stores are layered and duplicated, so it is not a singleton
export { createContextStore } from "./context"
// Initialises an app by loading screens and routes
export { initialise } from "./initialise"
// Derive the current role of the logged-in user, which may be overridden by
// dev tools
export const currentRole = derived(
[devToolsStore, authStore],
([$devToolsStore, $authStore]) => {
return $devToolsStore.role || $authStore?.roleId
}
)