Merge branch 'feat/user-groups-tab' of github.com:Budibase/budibase into feat/user-groups-tab

This commit is contained in:
Peter Clement 2022-07-11 15:30:31 +01:00
commit c084412a8d
5 changed files with 178 additions and 78 deletions

View File

@ -10,6 +10,7 @@
import Detail from "../../Typography/Detail.svelte" import Detail from "../../Typography/Detail.svelte"
export let primaryLabel = "" export let primaryLabel = ""
export let primaryValue = null
export let id = null export let id = null
export let placeholder = "Choose an option or type" export let placeholder = "Choose an option or type"
export let disabled = false export let disabled = false
@ -73,6 +74,11 @@
primaryOpen = false primaryOpen = false
} }
const onClearPrimary = () => {
dispatch("pickprimary", null)
primaryOpen = false
}
const onPickSecondary = newValue => { const onPickSecondary = newValue => {
dispatch("picksecondary", newValue) dispatch("picksecondary", newValue)
secondaryOpen = false secondaryOpen = false
@ -123,7 +129,7 @@
class:is-invalid={!!error} class:is-invalid={!!error}
class:is-disabled={disabled} class:is-disabled={disabled}
class:is-focused={focus} class:is-focused={focus}
style="width: 70%" class:is-full-width={!secondaryOptions.length}
> >
{#if iconData} {#if iconData}
<svg <svg
@ -153,6 +159,21 @@
class="spectrum-Textfield-input spectrum-InputGroup-input" class="spectrum-Textfield-input spectrum-InputGroup-input"
class:labelPadding={iconData} class:labelPadding={iconData}
/> />
{#if primaryValue}
<button
on:click={() => onClearPrimary()}
type="reset"
class="spectrum-ClearButton spectrum-Search-clearButton"
>
<svg
class="spectrum-Icon spectrum-UIIcon-Cross75"
focusable="false"
aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Cross75" />
</svg>
</button>
{/if}
</div> </div>
{#if primaryOpen} {#if primaryOpen}
<div <div
@ -160,7 +181,7 @@
transition:fly|local={{ y: -20, duration: 200 }} transition:fly|local={{ y: -20, duration: 200 }}
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open" class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"
class:auto-width={autoWidth} class:auto-width={autoWidth}
style="width: 70%" class:is-full-width={!secondaryOptions.length}
> >
<ul class="spectrum-Menu" role="listbox"> <ul class="spectrum-Menu" role="listbox">
{#if placeholderOption} {#if placeholderOption}
@ -250,78 +271,82 @@
</ul> </ul>
</div> </div>
{/if} {/if}
<div style="width: 30%"> {#if secondaryOptions.length}
<button <div style="width: 30%">
{id} <button
class="spectrum-Picker spectrum-Picker--sizeM override-borders" {id}
{disabled} class="spectrum-Picker spectrum-Picker--sizeM override-borders"
class:is-open={secondaryOpen} {disabled}
aria-haspopup="listbox" class:is-open={secondaryOpen}
on:mousedown={onClickSecondary} aria-haspopup="listbox"
> on:mousedown={onClickSecondary}
{#if secondaryFieldIcon}
<span class="option-left">
<Icon name={secondaryFieldIcon} />
</span>
{:else if secondaryFieldColour}
<span class="option-left">
<StatusLight color={secondaryFieldColour} />
</span>
{/if}
<span class:auto-width={autoWidth} class="spectrum-Picker-label">
{secondaryFieldText}
</span>
<svg
class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
focusable="false"
aria-hidden="true"
> >
<use xlink:href="#spectrum-css-icon-Chevron100" /> {#if secondaryFieldIcon}
</svg> <span class="option-left">
</button> <Icon name={secondaryFieldIcon} />
{#if secondaryOpen} </span>
<div {:else if secondaryFieldColour}
use:clickOutside={() => (secondaryOpen = false)} <span class="option-left">
transition:fly|local={{ y: -20, duration: 200 }} <StatusLight color={secondaryFieldColour} />
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open" </span>
style="width: 30%" {/if}
>
<ul class="spectrum-Menu" role="listbox">
{#each secondaryOptions as option, idx}
<li
class="spectrum-Menu-item"
class:is-selected={isOptionSelected(
getSecondaryOptionValue(option, idx)
)}
role="option"
aria-selected="true"
tabindex="0"
on:click={() =>
onPickSecondary(getSecondaryOptionValue(option, idx))}
>
{#if getSecondaryOptionColour(option, idx)}
<span class="option-left">
<StatusLight color={getSecondaryOptionColour(option, idx)} />
</span>
{/if}
<span class="spectrum-Menu-itemLabel"> <span class:auto-width={autoWidth} class="spectrum-Picker-label">
{getSecondaryOptionLabel(option, idx)} {secondaryFieldText}
</span> </span>
<svg <svg
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon" class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
focusable="false" focusable="false"
aria-hidden="true" aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Chevron100" />
</svg>
</button>
{#if secondaryOpen}
<div
use:clickOutside={() => (secondaryOpen = false)}
transition:fly|local={{ y: -20, duration: 200 }}
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"
style="width: 30%"
>
<ul class="spectrum-Menu" role="listbox">
{#each secondaryOptions as option, idx}
<li
class="spectrum-Menu-item"
class:is-selected={isOptionSelected(
getSecondaryOptionValue(option, idx)
)}
role="option"
aria-selected="true"
tabindex="0"
on:click={() =>
onPickSecondary(getSecondaryOptionValue(option, idx))}
> >
<use xlink:href="#spectrum-css-icon-Checkmark100" /> {#if getSecondaryOptionColour(option, idx)}
</svg> <span class="option-left">
</li> <StatusLight
{/each} color={getSecondaryOptionColour(option, idx)}
</ul> />
</div> </span>
{/if} {/if}
</div>
<span class="spectrum-Menu-itemLabel">
{getSecondaryOptionLabel(option, idx)}
</span>
<svg
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
focusable="false"
aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Checkmark100" />
</svg>
</li>
{/each}
</ul>
</div>
{/if}
</div>
{/if}
</div> </div>
<style> <style>
@ -381,4 +406,25 @@
.labelPadding { .labelPadding {
padding-left: calc(1em + 10px + 8px); padding-left: calc(1em + 10px + 8px);
} }
.spectrum-Textfield.spectrum-InputGroup-textfield {
width: 70%;
}
.spectrum-Textfield.spectrum-InputGroup-textfield.is-full-width {
width: 100%;
}
.spectrum-Textfield.spectrum-InputGroup-textfield.is-full-width input {
border-right-width: thin;
}
.spectrum-Popover.spectrum-Popover--bottom.spectrum-Picker-popover.is-open {
width: 70%;
}
.spectrum-Popover.spectrum-Popover--bottom.spectrum-Picker-popover.is-open.is-full-width {
width: 100%;
}
.spectrum-Search-clearButton {
position: absolute;
}
</style> </style>

View File

@ -71,9 +71,9 @@
} }
const onPickPrimary = e => { const onPickPrimary = e => {
primaryLabel = e.detail.label primaryLabel = e?.detail?.label || null
primaryValue = e.detail.value primaryValue = e?.detail?.value || null
dispatch("pickprimary", e.detail.value) dispatch("pickprimary", e?.detail?.value || {})
} }
const onPickSecondary = e => { const onPickSecondary = e => {

View File

@ -0,0 +1,43 @@
<script>
import { PickerDropdown, notifications } from "@budibase/bbui"
import { groups } from "stores/portal"
import { onMount, createEventDispatcher } from "svelte"
const dispatch = createEventDispatcher()
$: optionSections = {
groups: {
data: $groups,
getLabel: group => group.name,
getValue: group => group._id,
getIcon: group => group.icon,
getColour: group => group.color,
},
}
$: appData = [{ id: "", role: "" }]
$: onChange = selected => {
const { detail } = selected
if (!detail) return
const groupSelected = $groups.find(x => x._id === detail)
const appIds = groupSelected?.apps.map(x => x.appId) || null
dispatch("change", appIds)
}
onMount(async () => {
try {
await groups.actions.init()
} catch (error) {
notifications.error("Error")
}
})
</script>
<PickerDropdown
autocomplete
primaryOptions={optionSections}
placeholder={"Filter by access"}
on:pickprimary={onChange}
/>

View File

@ -20,12 +20,13 @@
import { store, automationStore } from "builderStore" import { store, automationStore } from "builderStore"
import { API } from "api" import { API } from "api"
import { onMount } from "svelte" import { onMount } from "svelte"
import { apps, auth, admin, templates } from "stores/portal" import { apps, auth, admin, templates, groups } from "stores/portal"
import download from "downloadjs" import download from "downloadjs"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import AppRow from "components/start/AppRow.svelte" import AppRow from "components/start/AppRow.svelte"
import { AppStatus } from "constants" import { AppStatus } from "constants"
import Logo from "assets/bb-space-man.svg" import Logo from "assets/bb-space-man.svg"
import AccessFilter from "./_components/AcessFilter.svelte"
let sortBy = "name" let sortBy = "name"
let template let template
@ -39,6 +40,7 @@
let cloud = $admin.cloud let cloud = $admin.cloud
let creatingFromTemplate = false let creatingFromTemplate = false
let automationErrors let automationErrors
let accessFilterList = null
const resolveWelcomeMessage = (auth, apps) => { const resolveWelcomeMessage = (auth, apps) => {
const userWelcome = auth?.user?.firstName const userWelcome = auth?.user?.firstName
@ -56,8 +58,10 @@
: "Start from scratch" : "Start from scratch"
$: enrichedApps = enrichApps($apps, $auth.user, sortBy) $: enrichedApps = enrichApps($apps, $auth.user, sortBy)
$: filteredApps = enrichedApps.filter(app => $: filteredApps = enrichedApps.filter(
app?.name?.toLowerCase().includes(searchTerm.toLowerCase()) app =>
app?.name?.toLowerCase().includes(searchTerm.toLowerCase()) &&
(accessFilterList !== null ? accessFilterList.includes(app?.appId) : true)
) )
$: lockedApps = filteredApps.filter(app => app?.lockedYou || app?.lockedOther) $: lockedApps = filteredApps.filter(app => app?.lockedYou || app?.lockedOther)
@ -202,6 +206,10 @@
$goto(`../../app/${app.devId}`) $goto(`../../app/${app.devId}`)
} }
const accessFilterAction = accessFilter => {
accessFilterList = accessFilter.detail
}
function createAppFromTemplateUrl(templateKey) { function createAppFromTemplateUrl(templateKey) {
// validate the template key just to make sure // validate the template key just to make sure
const templateParts = templateKey.split("/") const templateParts = templateKey.split("/")
@ -347,6 +355,9 @@
</Button> </Button>
{/if} {/if}
<div class="filter"> <div class="filter">
{#if $groups.length}
<AccessFilter on:change={accessFilterAction} />
{/if}
<Select <Select
quiet quiet
autoWidth autoWidth

View File

@ -42,7 +42,7 @@
<PickerDropdown <PickerDropdown
autocomplete autocomplete
primaryOptions={optionSections} primaryOptions={optionSections}
primaryPlaceholder={"Search Users"} placeholder={"Search Users"}
secondaryOptions={$roles} secondaryOptions={$roles}
bind:primaryValue={input.id} bind:primaryValue={input.id}
bind:secondaryValue={input.role} bind:secondaryValue={input.role}