Merge branch 'spectrum-bbui' into spectrum/kevs-bits-and-bobs-the-sequel

This commit is contained in:
Keviin Åberg Kultalahti 2021-04-27 15:28:03 +02:00
commit 0a036e5f20
17 changed files with 203 additions and 385 deletions

View File

@ -23,15 +23,13 @@
class:active class:active
class="spectrum-Button spectrum-Button--size{size.toUpperCase()}" class="spectrum-Button spectrum-Button--size{size.toUpperCase()}"
{disabled} {disabled}
on:click|preventDefault on:click|preventDefault>
>
{#if icon} {#if icon}
<svg <svg
class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}" class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}"
focusable="false" focusable="false"
aria-hidden="true" aria-hidden="true"
aria-label={icon} aria-label={icon}>
>
<use xlink:href="#spectrum-icon-18-{icon}" /> <use xlink:href="#spectrum-icon-18-{icon}" />
</svg> </svg>
{/if} {/if}
@ -46,4 +44,7 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.active {
color: var(--spectrum-global-color-blue-600) !important;
}
</style> </style>

View File

@ -3,7 +3,7 @@
</script> </script>
<label <label
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel`}> class={`spectrum-FieldLabel spectrum-FieldLabel--sizeL spectrum-Form-itemLabel`}>
<slot /> <slot />
</label> </label>

View File

@ -1,20 +1,21 @@
<script> <script>
import { Popover, Button } from "@budibase/bbui" import { Modal, Button } from "@budibase/bbui"
import CalculatePopover from "../popovers/CalculatePopover.svelte" import CalculateModal from "../modals/CalculateModal.svelte"
export let view = {} export let view = {}
let anchor let modal
let dropdown
</script> </script>
<div bind:this={anchor}> <Button
<Button icon="Calculator" primary size="S" quiet icon="Calculator"
on:click={dropdown.show} primary
active={view.field && view.calculation}> size="S"
Calculate quiet
</Button> on:click={modal.show}
</div> active={view.field && view.calculation}>
<Popover bind:this={dropdown} {anchor} align="left"> Calculate
<CalculatePopover {view} onClosed={dropdown.hide} /> </Button>
</Popover> <Modal bind:this={modal}>
<CalculateModal {view} />
</Modal>

View File

@ -1,16 +1,13 @@
<script> <script>
import { Popover, Button } from "@budibase/bbui" import { Modal, Button } from "@budibase/bbui"
import CreateViewPopover from "../popovers/CreateViewPopover.svelte" import CreateViewModal from "../modals/CreateViewModal.svelte"
let anchor let modal
let dropdown
</script> </script>
<div bind:this={anchor}> <Button icon="CollectionAdd" primary size="S" quiet on:click={modal.show}>
<Button icon="CollectionAdd" primary size="S" quiet on:click={dropdown.show}> Create New View
Create New View </Button>
</Button> <Modal bind:this={modal}>
</div> <CreateViewModal />
<Popover bind:this={dropdown} {anchor} align="left"> </Modal>
<CreateViewPopover onClosed={dropdown.hide} />
</Popover>

View File

@ -1,18 +1,15 @@
<script> <script>
import { Button, Icon, Popover } from "@budibase/bbui" import { Button, Modal } from "@budibase/bbui"
import ExportPopover from "../popovers/ExportPopover.svelte" import ExportModal from "../modals/ExportModal.svelte"
export let view export let view
let anchor let modal
let dropdown
</script> </script>
<div bind:this={anchor}> <Button icon="Download" primary size="S" quiet on:click={modal.show}>
<Button icon="Download" primary size="S" quiet on:click={dropdown.show}> Export
Export </Button>
</Button> <Modal bind:this={modal}>
</div> <ExportModal {view} />
<Popover bind:this={dropdown} {anchor} align="left"> </Modal>
<ExportPopover {view} onClosed={dropdown.hide} />
</Popover>

View File

@ -1,20 +1,21 @@
<script> <script>
import { Popover, Button, Icon } from "@budibase/bbui" import { Button, Modal } from "@budibase/bbui"
import FilterPopover from "../popovers/FilterPopover.svelte" import FilterModal from "../modals/FilterModal.svelte"
export let view = {} export let view = {}
let anchor let modal
let dropdown
</script> </script>
<div bind:this={anchor}> <Button
<Button icon="Filter" primary size="S" quiet icon="Filter"
on:click={dropdown.show} primary
active={view.filters && view.filters.length}> size="S"
Filter quiet
</Button> on:click={modal.show}
</div> active={view.filters?.length}>
<Popover bind:this={dropdown} {anchor} align="left"> Filter
<FilterPopover {view} onClosed={dropdown.hide} /> </Button>
</Popover> <Modal bind:this={modal}>
<FilterModal {view} />
</Modal>

View File

@ -1,18 +1,21 @@
<script> <script>
import { Popover, Button } from "@budibase/bbui" import { Modal, Button } from "@budibase/bbui"
import GroupByPopover from "../popovers/GroupByPopover.svelte" import GroupByModal from "../modals/GroupByModal.svelte"
export let view = {} export let view = {}
let anchor let modal
let dropdown
</script> </script>
<div bind:this={anchor}> <Button
<Button icon="Group" primary size="S" quiet active={!!view.groupBy} on:click={dropdown.show}> icon="Group"
Group By primary
</Button> size="S"
</div> quiet
<Popover bind:this={dropdown} {anchor} align="left"> active={!!view.groupBy}
<GroupByPopover {view} onClosed={dropdown.hide} /> on:click={modal.show}>
</Popover> Group By
</Button>
<Modal bind:this={modal}>
<GroupByModal {view} />
</Modal>

View File

@ -1,29 +1,25 @@
<script> <script>
import { Button, Popover } from "@budibase/bbui" import { Button, Modal } from "@budibase/bbui"
import { permissions } from "stores/backend" import { permissions } from "stores/backend"
import ManageAccessPopover from "../popovers/ManageAccessPopover.svelte" import ManageAccessModal from "../modals/ManageAccessModal.svelte"
export let resourceId export let resourceId
let anchor let modal
let dropdown
let resourcePermissions let resourcePermissions
async function openDropdown() { async function openDropdown() {
resourcePermissions = await permissions.forResource(resourceId) resourcePermissions = await permissions.forResource(resourceId)
dropdown.show() modal.show()
} }
</script> </script>
<div bind:this={anchor}> <Button icon="LockClosed" primary size="S" quiet on:click={openDropdown}>
<Button icon="LockClosed" primary size="S" quiet on:click={openDropdown}> Manage Access
Manage Access </Button>
</Button> <Modal bind:this={modal}>
</div> <ManageAccessModal
<Popover bind:this={dropdown} {anchor} align="left">
<ManageAccessPopover
{resourceId} {resourceId}
levels={$permissions} levels={$permissions}
permissions={resourcePermissions} permissions={resourcePermissions} />
onClosed={dropdown.hide} /> </Modal>
</Popover>

View File

@ -1,5 +1,5 @@
<script> <script>
import { Button, Select, Label, notifications } from "@budibase/bbui" import { Select, Label, notifications, ModalContent } from "@budibase/bbui"
import { tables, views } from "stores/backend" import { tables, views } from "stores/backend"
import analytics from "analytics" import analytics from "analytics"
@ -35,13 +35,15 @@
function saveView() { function saveView() {
views.save(view) views.save(view)
notifications.success(`View ${view.name} saved.`) notifications.success(`View ${view.name} saved.`)
onClosed()
analytics.captureEvent("Added View Calculate", { field: view.field }) analytics.captureEvent("Added View Calculate", { field: view.field })
} }
</script> </script>
<div class="actions"> <ModalContent
<h5>Calculate</h5> title="Calculate"
confirmText="Save"
onConfirm={saveView}
disabled={!view.field}>
<div class="input-group-row"> <div class="input-group-row">
<Label>The</Label> <Label>The</Label>
<Select <Select
@ -54,31 +56,9 @@
<Select bind:value={view.field} options={fields} /> <Select bind:value={view.field} options={fields} />
{/if} {/if}
</div> </div>
<div class="footer"> </ModalContent>
<Button secondary on:click={onClosed}>Cancel</Button>
<Button cta on:click={saveView} disabled={!view.field}>Save</Button>
</div>
</div>
<style> <style>
.actions {
width: 200px;
display: grid;
grid-gap: var(--spacing-xl);
padding: var(--spacing-xl);
}
h5 {
margin: 0;
font-weight: 500;
}
.footer {
display: flex;
justify-content: flex-end;
gap: var(--spacing-m);
}
.input-group-row { .input-group-row {
display: grid; display: grid;
grid-template-columns: auto 1fr; grid-template-columns: auto 1fr;

View File

@ -1,5 +1,5 @@
<script> <script>
import { Button, Input, notifications, Heading } from "@budibase/bbui" import { Input, notifications, ModalContent } from "@budibase/bbui"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import { views as viewsStore } from "stores/backend" import { views as viewsStore } from "stores/backend"
import { tables } from "stores/backend" import { tables } from "stores/backend"
@ -23,37 +23,14 @@
field, field,
}) })
notifications.success(`View ${name} created`) notifications.success(`View ${name} created`)
onClosed()
analytics.captureEvent("View Created", { name }) analytics.captureEvent("View Created", { name })
$goto(`../../view/${name}`) $goto(`../../view/${name}`)
} }
</script> </script>
<div class="actions"> <ModalContent
<Heading s>Create View</Heading> title="Create View"
confirmText="Create View"
onConfirm={saveView}>
<Input label="View Name" thin bind:value={name} /> <Input label="View Name" thin bind:value={name} />
<div class="footer"> </ModalContent>
<Button secondary on:click={onClosed}>Cancel</Button>
<Button cta on:click={saveView}>Save View</Button>
</div>
</div>
<style>
h5 {
margin: 0;
font-weight: 500;
}
.actions {
display: grid;
grid-gap: var(--spacing-xl);
padding: var(--spacing-xl);
width: 240px;
}
.footer {
display: flex;
justify-content: flex-end;
gap: var(--spacing-m);
}
</style>

View File

@ -21,7 +21,7 @@
// Changes the selected role // Changes the selected role
const changeRole = event => { const changeRole = event => {
const id = event?.target?.value const id = event?.detail
const role = $roles.find(role => role._id === id) const role = $roles.find(role => role._id === id)
if (role) { if (role) {
selectedRole = { selectedRole = {
@ -94,42 +94,34 @@
secondary secondary
label="Role" label="Role"
value={selectedRoleId} value={selectedRoleId}
on:change={changeRole}> on:change={changeRole}
<option value="">Create new role</option> options={$roles}
{#each $roles as role} placeholder="Create new role"
<option value={role._id}>{role.name}</option> getOptionValue={role => role._id}
{/each} getOptionLabel={role => role.name} />
</Select>
{#if selectedRole} {#if selectedRole}
<Input <Input
label="Name" label="Name"
bind:value={selectedRole.name} bind:value={selectedRole.name}
thin
disabled={builtInRoles.includes(selectedRole.name)} /> disabled={builtInRoles.includes(selectedRole.name)} />
<Select <Select
thin
secondary
label="Inherits Role" label="Inherits Role"
bind:value={selectedRole.inherits}> bind:value={selectedRole.inherits}
<option value="">None</option> options={otherRoles}
{#each otherRoles as role} getOptionValue={role => role._id}
<option value={role._id}>{role.name}</option> getOptionLabel={role => role.name}
{/each} placeholder="None" />
</Select>
<Select <Select
thin
secondary
label="Base Permissions" label="Base Permissions"
bind:value={selectedRole.permissionId}> bind:value={selectedRole.permissionId}
<option value="">Choose permissions</option> options={basePermissions}
{#each basePermissions as basePerm} getOptionValue={x => x._id}
<option value={basePerm._id}>{basePerm.name}</option> getOptionLabel={x => x.name}
{/each} placeholder="Choose permissions" />
</Select>
{/if} {/if}
<div slot="footer"> <div slot="footer">
{#if !isCreating} {#if !isCreating}
<Button red on:click={deleteRole}>Delete</Button> <Button warning on:click={deleteRole}>Delete</Button>
{/if} {/if}
</div> </div>
</ModalContent> </ModalContent>

View File

@ -0,0 +1,37 @@
<script>
import { Select, ModalContent } from "@budibase/bbui"
import download from "downloadjs"
const FORMATS = [
{
name: "CSV",
key: "csv",
},
{
name: "JSON",
key: "json",
},
]
export let view
let exportFormat = FORMATS[0].key
async function exportView() {
download(
`/api/views/export?view=${encodeURIComponent(
view.name
)}&format=${exportFormat}`
)
}
</script>
<ModalContent title="Export Data" confirmText="Export" onConfirm={exportView}>
<Select
label="Format"
bind:value={exportFormat}
options={FORMATS}
placeholder={null}
getOptionLabel={x => x.name}
getOptionValue={x => x.key} />
</ModalContent>

View File

@ -1,7 +1,16 @@
<script> <script>
import { Button, Input, Select, DatePicker } from "@budibase/bbui" import {
Button,
Input,
Body,
Select,
DatePicker,
ModalContent,
Label,
notifications,
Icon,
} from "@budibase/bbui"
import { tables, views } from "stores/backend" import { tables, views } from "stores/backend"
import { notifications } from "@budibase/bbui"
import analytics from "analytics" import analytics from "analytics"
const CONDITIONS = [ const CONDITIONS = [
@ -55,7 +64,6 @@
function saveView() { function saveView() {
views.save(view) views.save(view)
notifications.success(`View ${view.name} saved.`) notifications.success(`View ${view.name} saved.`)
onClosed()
analytics.captureEvent("Added View Filter", { analytics.captureEvent("Added View Filter", {
filters: JSON.stringify(view.filters), filters: JSON.stringify(view.filters),
}) })
@ -67,7 +75,7 @@
} }
function addFilter() { function addFilter() {
view.filters.push({}) view.filters.push({ conjunction: "AND" })
view.filters = view.filters view.filters = view.filters
} }
@ -98,9 +106,8 @@
// reset if type changed // reset if type changed
if ( if (
filter.key && filter.key &&
ev.target.value && ev.detail &&
viewTable.schema[filter.key].type !== viewTable.schema[filter.key].type !== viewTable.schema[ev.detail].type
viewTable.schema[ev.target.value].type
) { ) {
filter.value = "" filter.value = ""
} }
@ -110,17 +117,21 @@
const getOptionValue = x => x.key const getOptionValue = x => x.key
</script> </script>
<div class="actions"> <ModalContent
<h5>Filter</h5> title="Filter"
confirmText="Save"
onConfirm={saveView}
size="large">
{#if view.filters.length} {#if view.filters.length}
<div class="input-group-row"> <div class="input-group-row">
{#each view.filters as filter, idx} {#each view.filters as filter, idx}
{#if idx === 0} {#if idx === 0}
<p>Where</p> <Label>Where</Label>
{:else} {:else}
<Select <Select
bind:value={filter.conjunction} bind:value={filter.conjunction}
options={CONJUNCTIONS} options={CONJUNCTIONS}
placeholder={null}
{getOptionLabel} {getOptionLabel}
{getOptionValue} /> {getOptionValue} />
{/if} {/if}
@ -152,61 +163,22 @@
placeholder={filter.key || fields[0]} placeholder={filter.key || fields[0]}
bind:value={filter.value} /> bind:value={filter.value} />
{/if} {/if}
<i class="ri-close-circle-fill" on:click={() => removeFilter(idx)} /> <Icon hoverable name="Close" on:click={() => removeFilter(idx)} />
{/each} {/each}
</div> </div>
{:else}
<Body s>Add a filter to get started.</Body>
{/if} {/if}
<div class="footer"> <div slot="footer">
<div class="add-filter"> <Button secondary on:click={addFilter}>Add Filter</Button>
<Button text on:click={addFilter}>Add Filter</Button>
</div>
<div class="buttons">
<Button secondary on:click={onClosed}>Cancel</Button>
<Button cta on:click={saveView}>Save</Button>
</div>
</div> </div>
</div> </ModalContent>
<style> <style>
.actions {
display: grid;
grid-gap: var(--spacing-xl);
padding: var(--spacing-xl);
}
h5 {
margin: 0;
font-weight: 500;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
}
.buttons {
display: flex;
justify-content: flex-end;
gap: var(--spacing-m);
}
.ri-close-circle-fill {
cursor: pointer;
}
.input-group-row { .input-group-row {
display: grid; display: grid;
grid-template-columns: minmax(50px, auto) 1fr 1fr 1fr 15px; grid-template-columns: minmax(70px, auto) 1fr 1fr 1fr 15px;
gap: var(--spacing-s); gap: var(--spacing-s);
align-items: center; align-items: center;
} }
p {
margin: 0;
font-size: var(--font-size-xs);
}
.add-filter {
margin-right: 16px;
}
</style> </style>

View File

@ -0,0 +1,24 @@
<script>
import { Button, Select, ModalContent, notifications } from "@budibase/bbui"
import { tables, views } from "stores/backend"
import { FIELDS } from "constants/backend"
export let view = {}
export let onClosed
$: viewTable = $tables.list.find(({ _id }) => _id === $views.selected.tableId)
$: fields =
viewTable &&
Object.entries(viewTable.schema)
.filter(entry => entry[1].type !== FIELDS.LINK.type)
.map(([key]) => key)
function saveView() {
views.save(view)
notifications.success(`View ${view.name} saved.`)
}
</script>
<ModalContent title="Group By" confirmText="Save" onConfirm={saveView}>
<Select bind:value={view.groupBy} options={fields} />
</ModalContent>

View File

@ -1,14 +1,13 @@
<script> <script>
import { roles, permissions as permissionsStore } from "stores/backend" import { roles, permissions as permissionsStore } from "stores/backend"
import { import {
Button,
Label, Label,
Input, Input,
Select, Select,
Spacer, Spacer,
notifications, notifications,
Heading,
Body, Body,
ModalContent,
} from "@budibase/bbui" } from "@budibase/bbui"
import { capitalise } from "../../../../helpers" import { capitalise } from "../../../../helpers"
@ -31,12 +30,8 @@
} }
</script> </script>
<div class="popover"> <ModalContent title="Manage Access" showCancelButton={false} confirmText="Done">
<Heading s>Who Can Access This Data?</Heading> <Body s>Specify the minimum access level role for this data.</Body>
<div class="note">
<Body s>Specify the minimum access level role for this data.</Body>
</div>
<Spacer large />
<div class="row"> <div class="row">
<Label extraSmall grey>Level</Label> <Label extraSmall grey>Level</Label>
<Label extraSmall grey>Role</Label> <Label extraSmall grey>Role</Label>
@ -50,41 +45,12 @@
getOptionValue={x => x._id} /> getOptionValue={x => x._id} />
{/each} {/each}
</div> </div>
</ModalContent>
<Spacer large />
<div class="footer">
<Button secondary on:click={onClosed}>Cancel</Button>
</div>
</div>
<style> <style>
.popover {
display: grid;
width: 400px;
padding: var(--spacing-xl);
}
h5 {
margin: 0;
font-weight: 500;
}
.footer {
display: flex;
justify-content: flex-end;
gap: var(--spacing-m);
margin-top: var(--spacing-l);
}
.row { .row {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
grid-gap: var(--spacing-s); grid-gap: var(--spacing-s);
} }
.note {
margin-top: 10px;
margin-bottom: 0;
}
</style> </style>

View File

@ -1,63 +0,0 @@
<script>
import { Button, Select, Heading } from "@budibase/bbui"
import download from "downloadjs"
const FORMATS = [
{
name: "CSV",
key: "csv",
},
{
name: "JSON",
key: "json",
},
]
export let view
export let onClosed
let exportFormat = FORMATS[0].key
async function exportView() {
download(
`/api/views/export?view=${encodeURIComponent(
view.name
)}&format=${exportFormat}`
)
onClosed()
}
</script>
<div class="popover">
<Heading s>Export Data</Heading>
<Select
label="Format"
bind:value={exportFormat}
options={FORMATS}
getOptionLabel={x => x.name}
getOptionValue={x => x.key} />
<div class="footer">
<Button secondary on:click={onClosed}>Cancel</Button>
<Button cta on:click={exportView}>Export</Button>
</div>
</div>
<style>
.popover {
display: grid;
grid-gap: var(--spacing-xl);
padding: var(--spacing-xl);
width: 240px;
}
h5 {
margin: 0;
font-weight: 500;
}
.footer {
display: flex;
justify-content: flex-end;
gap: var(--spacing-m);
}
</style>

View File

@ -1,63 +0,0 @@
<script>
import { Button, Select } from "@budibase/bbui"
import { tables, views } from "stores/backend"
import { notifications } from "@budibase/bbui"
import { FIELDS } from "constants/backend"
export let view = {}
export let onClosed
$: viewTable = $tables.list.find(({ _id }) => _id === $views.selected.tableId)
$: fields =
viewTable &&
Object.entries(viewTable.schema)
.filter(entry => entry[1].type !== FIELDS.LINK.type)
.map(([key]) => key)
function saveView() {
views.save(view)
notifications.success(`View ${view.name} saved.`)
onClosed()
}
</script>
<div class="actions">
<h5>Group By</h5>
<Select bind:value={view.groupBy} options={fields} />
<div class="footer">
<Button secondary on:click={onClosed}>Cancel</Button>
<Button cta on:click={saveView}>Save</Button>
</div>
</div>
<style>
.actions {
display: grid;
width: 200px;
grid-gap: var(--spacing-xl);
padding: var(--spacing-xl);
}
h5 {
margin: 0;
font-weight: 500;
}
.footer {
display: flex;
justify-content: flex-end;
gap: var(--spacing-m);
}
.input-group-row {
display: grid;
grid-template-columns: 20px 1fr;
gap: var(--spacing-s);
align-items: center;
}
p {
margin: 0;
font-size: var(--font-size-xs);
}
</style>