Add role setting to navigation links to allow easily customising which roles see which links
This commit is contained in:
parent
5a35c182c1
commit
3d1c5111e9
|
@ -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 {
|
||||
|
|
|
@ -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}"
|
||||
>
|
||||
<DeviceBindingsProvider>
|
||||
<UserBindingsProvider>
|
||||
{#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>
|
||||
<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}
|
||||
<UserBindingsProvider>
|
||||
<DeviceBindingsProvider>
|
||||
<StateBindingsProvider>
|
||||
<RowSelectionProvider>
|
||||
<!-- Settings bar can be rendered outside of device preview -->
|
||||
|
@ -198,9 +206,9 @@
|
|||
</div>
|
||||
</RowSelectionProvider>
|
||||
</StateBindingsProvider>
|
||||
</DeviceBindingsProvider>
|
||||
</UserBindingsProvider>
|
||||
{/if}
|
||||
</UserBindingsProvider>
|
||||
</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);
|
||||
|
|
|
@ -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("/")
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue