Merge branch 'develop' of github.com:Budibase/budibase into feature/multi-tenants
This commit is contained in:
commit
e2e796cd9d
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "0.9.79-alpha.4",
|
"version": "0.9.80-alpha.9",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/auth",
|
"name": "@budibase/auth",
|
||||||
"version": "0.9.79-alpha.4",
|
"version": "0.9.80-alpha.9",
|
||||||
"description": "Authentication middlewares for budibase builder and apps",
|
"description": "Authentication middlewares for budibase builder and apps",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/bbui",
|
"name": "@budibase/bbui",
|
||||||
"description": "A UI solution used in the different Budibase projects.",
|
"description": "A UI solution used in the different Budibase projects.",
|
||||||
"version": "0.9.79-alpha.4",
|
"version": "0.9.80-alpha.9",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"svelte": "src/index.js",
|
"svelte": "src/index.js",
|
||||||
"module": "dist/bbui.es.js",
|
"module": "dist/bbui.es.js",
|
||||||
|
|
|
@ -115,7 +115,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.spectrum-Modal {
|
.spectrum-Modal {
|
||||||
background: var(--background);
|
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
max-height: none;
|
max-height: none;
|
||||||
margin: 40px 0;
|
margin: 40px 0;
|
||||||
|
|
|
@ -88,7 +88,6 @@
|
||||||
display: grid;
|
display: grid;
|
||||||
position: relative;
|
position: relative;
|
||||||
gap: var(--spacing-xl);
|
gap: var(--spacing-xl);
|
||||||
color: var(--ink);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.spectrum-Dialog-content {
|
.spectrum-Dialog-content {
|
||||||
|
@ -106,13 +105,8 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 15px;
|
top: 15px;
|
||||||
right: 15px;
|
right: 15px;
|
||||||
color: var(--ink);
|
|
||||||
font-size: var(--font-size-m);
|
font-size: var(--font-size-m);
|
||||||
}
|
}
|
||||||
.close-icon:hover {
|
|
||||||
color: var(--grey-6);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.close-icon :global(svg) {
|
.close-icon :global(svg) {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "0.9.79-alpha.4",
|
"version": "0.9.80-alpha.9",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -65,10 +65,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^0.9.79-alpha.4",
|
"@budibase/bbui": "^0.9.80-alpha.9",
|
||||||
"@budibase/client": "^0.9.79-alpha.4",
|
"@budibase/client": "^0.9.80-alpha.9",
|
||||||
"@budibase/colorpicker": "1.1.2",
|
"@budibase/colorpicker": "1.1.2",
|
||||||
"@budibase/string-templates": "^0.9.79-alpha.4",
|
"@budibase/string-templates": "^0.9.80-alpha.9",
|
||||||
"@sentry/browser": "5.19.1",
|
"@sentry/browser": "5.19.1",
|
||||||
"@spectrum-css/page": "^3.0.1",
|
"@spectrum-css/page": "^3.0.1",
|
||||||
"@spectrum-css/vars": "^3.0.1",
|
"@spectrum-css/vars": "^3.0.1",
|
||||||
|
|
|
@ -530,6 +530,11 @@ export const getFrontendStore = () => {
|
||||||
selected._styles = { normal: {}, hover: {}, active: {} }
|
selected._styles = { normal: {}, hover: {}, active: {} }
|
||||||
await store.actions.preview.saveSelected()
|
await store.actions.preview.saveSelected()
|
||||||
},
|
},
|
||||||
|
updateConditions: async conditions => {
|
||||||
|
const selected = get(selectedComponent)
|
||||||
|
selected._conditions = conditions
|
||||||
|
await store.actions.preview.saveSelected()
|
||||||
|
},
|
||||||
updateProp: async (name, value) => {
|
updateProp: async (name, value) => {
|
||||||
let component = get(selectedComponent)
|
let component = get(selectedComponent)
|
||||||
if (!name || !component) {
|
if (!name || !component) {
|
||||||
|
|
|
@ -6,8 +6,10 @@
|
||||||
import ErrorsBox from "components/common/ErrorsBox.svelte"
|
import ErrorsBox from "components/common/ErrorsBox.svelte"
|
||||||
import { roles } from "stores/backend"
|
import { roles } from "stores/backend"
|
||||||
|
|
||||||
|
const BASE_ROLE = { _id: "", inherits: "BASIC", permissionId: "Read/Write" }
|
||||||
|
|
||||||
let basePermissions = []
|
let basePermissions = []
|
||||||
let selectedRole = {}
|
let selectedRole = BASE_ROLE
|
||||||
let errors = []
|
let errors = []
|
||||||
let builtInRoles = ["Admin", "Power", "Basic", "Public"]
|
let builtInRoles = ["Admin", "Power", "Basic", "Public"]
|
||||||
// Don't allow editing of public role
|
// Don't allow editing of public role
|
||||||
|
@ -15,6 +17,11 @@
|
||||||
$: selectedRoleId = selectedRole._id
|
$: selectedRoleId = selectedRole._id
|
||||||
$: otherRoles = editableRoles.filter(role => role._id !== selectedRoleId)
|
$: otherRoles = editableRoles.filter(role => role._id !== selectedRoleId)
|
||||||
$: isCreating = selectedRoleId == null || selectedRoleId === ""
|
$: isCreating = selectedRoleId == null || selectedRoleId === ""
|
||||||
|
$: valid =
|
||||||
|
selectedRole.name &&
|
||||||
|
selectedRole.inherits &&
|
||||||
|
selectedRole.permissionId &&
|
||||||
|
!builtInRoles.includes(selectedRole.name)
|
||||||
|
|
||||||
const fetchBasePermissions = async () => {
|
const fetchBasePermissions = async () => {
|
||||||
const permissionsResponse = await api.get("/api/permission/builtin")
|
const permissionsResponse = await api.get("/api/permission/builtin")
|
||||||
|
@ -32,7 +39,7 @@
|
||||||
permissionId: role.permissionId ?? "",
|
permissionId: role.permissionId ?? "",
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
selectedRole = { _id: "", inherits: "", permissionId: "" }
|
selectedRole = BASE_ROLE
|
||||||
}
|
}
|
||||||
errors = []
|
errors = []
|
||||||
}
|
}
|
||||||
|
@ -88,6 +95,7 @@
|
||||||
title="Edit Roles"
|
title="Edit Roles"
|
||||||
confirmText={isCreating ? "Create" : "Save"}
|
confirmText={isCreating ? "Create" : "Save"}
|
||||||
onConfirm={saveRole}
|
onConfirm={saveRole}
|
||||||
|
disabled={!valid}
|
||||||
>
|
>
|
||||||
{#if errors.length}
|
{#if errors.length}
|
||||||
<ErrorsBox {errors} />
|
<ErrorsBox {errors} />
|
||||||
|
@ -115,7 +123,7 @@
|
||||||
options={otherRoles}
|
options={otherRoles}
|
||||||
getOptionValue={role => role._id}
|
getOptionValue={role => role._id}
|
||||||
getOptionLabel={role => role.name}
|
getOptionLabel={role => role.name}
|
||||||
placeholder="None"
|
disabled={builtInRoles.includes(selectedRole.name)}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
label="Base Permissions"
|
label="Base Permissions"
|
||||||
|
@ -123,7 +131,7 @@
|
||||||
options={basePermissions}
|
options={basePermissions}
|
||||||
getOptionValue={x => x._id}
|
getOptionValue={x => x._id}
|
||||||
getOptionLabel={x => x.name}
|
getOptionLabel={x => x.name}
|
||||||
placeholder="Choose permissions"
|
disabled={builtInRoles.includes(selectedRole.name)}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<div slot="footer">
|
<div slot="footer">
|
||||||
|
|
|
@ -1,32 +1,12 @@
|
||||||
<script>
|
<script>
|
||||||
import { isEmpty } from "lodash/fp"
|
import { isEmpty } from "lodash/fp"
|
||||||
import { Checkbox, Input, Select, DetailSummary } from "@budibase/bbui"
|
import { Input, DetailSummary } from "@budibase/bbui"
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
import PropertyControl from "./PropertyControls/PropertyControl.svelte"
|
import PropertyControl from "./PropertyControls/PropertyControl.svelte"
|
||||||
import LayoutSelect from "./PropertyControls/LayoutSelect.svelte"
|
import LayoutSelect from "./PropertyControls/LayoutSelect.svelte"
|
||||||
import RoleSelect from "./PropertyControls/RoleSelect.svelte"
|
import RoleSelect from "./PropertyControls/RoleSelect.svelte"
|
||||||
import TableSelect from "./PropertyControls/TableSelect.svelte"
|
|
||||||
import DataSourceSelect from "./PropertyControls/DataSourceSelect.svelte"
|
|
||||||
import DataProviderSelect from "./PropertyControls/DataProviderSelect.svelte"
|
|
||||||
import FieldSelect from "./PropertyControls/FieldSelect.svelte"
|
|
||||||
import MultiFieldSelect from "./PropertyControls/MultiFieldSelect.svelte"
|
|
||||||
import SchemaSelect from "./PropertyControls/SchemaSelect.svelte"
|
|
||||||
import SectionSelect from "./PropertyControls/SectionSelect.svelte"
|
|
||||||
import NavigationEditor from "./PropertyControls/NavigationEditor/NavigationEditor.svelte"
|
|
||||||
import EventsEditor from "./PropertyControls/EventsEditor"
|
|
||||||
import FilterEditor from "./PropertyControls/FilterEditor/FilterEditor.svelte"
|
|
||||||
import { IconSelect } from "./PropertyControls/IconSelect"
|
|
||||||
import StringFieldSelect from "./PropertyControls/StringFieldSelect.svelte"
|
|
||||||
import NumberFieldSelect from "./PropertyControls/NumberFieldSelect.svelte"
|
|
||||||
import OptionsFieldSelect from "./PropertyControls/OptionsFieldSelect.svelte"
|
|
||||||
import BooleanFieldSelect from "./PropertyControls/BooleanFieldSelect.svelte"
|
|
||||||
import LongFormFieldSelect from "./PropertyControls/LongFormFieldSelect.svelte"
|
|
||||||
import DateTimeFieldSelect from "./PropertyControls/DateTimeFieldSelect.svelte"
|
|
||||||
import AttachmentFieldSelect from "./PropertyControls/AttachmentFieldSelect.svelte"
|
|
||||||
import RelationshipFieldSelect from "./PropertyControls/RelationshipFieldSelect.svelte"
|
|
||||||
import ResetFieldsButton from "./PropertyControls/ResetFieldsButton.svelte"
|
import ResetFieldsButton from "./PropertyControls/ResetFieldsButton.svelte"
|
||||||
import ColorPicker from "./PropertyControls/ColorPicker.svelte"
|
import { getComponentForSettingType } from "./PropertyControls/componentSettings"
|
||||||
import URLSelect from "./PropertyControls/URLSelect.svelte"
|
|
||||||
|
|
||||||
export let componentDefinition
|
export let componentDefinition
|
||||||
export let componentInstance
|
export let componentInstance
|
||||||
|
@ -45,40 +25,9 @@
|
||||||
$: assetDefinition = isLayout ? layoutDefinition : screenDefinition
|
$: assetDefinition = isLayout ? layoutDefinition : screenDefinition
|
||||||
|
|
||||||
const updateProp = store.actions.components.updateProp
|
const updateProp = store.actions.components.updateProp
|
||||||
const controlMap = {
|
|
||||||
text: Input,
|
|
||||||
select: Select,
|
|
||||||
dataSource: DataSourceSelect,
|
|
||||||
dataProvider: DataProviderSelect,
|
|
||||||
boolean: Checkbox,
|
|
||||||
number: Input,
|
|
||||||
event: EventsEditor,
|
|
||||||
table: TableSelect,
|
|
||||||
color: ColorPicker,
|
|
||||||
icon: IconSelect,
|
|
||||||
field: FieldSelect,
|
|
||||||
multifield: MultiFieldSelect,
|
|
||||||
schema: SchemaSelect,
|
|
||||||
section: SectionSelect,
|
|
||||||
navigation: NavigationEditor,
|
|
||||||
filter: FilterEditor,
|
|
||||||
url: URLSelect,
|
|
||||||
"field/string": StringFieldSelect,
|
|
||||||
"field/number": NumberFieldSelect,
|
|
||||||
"field/options": OptionsFieldSelect,
|
|
||||||
"field/boolean": BooleanFieldSelect,
|
|
||||||
"field/longform": LongFormFieldSelect,
|
|
||||||
"field/datetime": DateTimeFieldSelect,
|
|
||||||
"field/attachment": AttachmentFieldSelect,
|
|
||||||
"field/link": RelationshipFieldSelect,
|
|
||||||
}
|
|
||||||
|
|
||||||
const getControl = type => {
|
|
||||||
return controlMap[type]
|
|
||||||
}
|
|
||||||
|
|
||||||
const canRenderControl = setting => {
|
const canRenderControl = setting => {
|
||||||
const control = getControl(setting?.type)
|
const control = getComponentForSettingType(setting?.type)
|
||||||
if (!control) {
|
if (!control) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -101,11 +50,11 @@
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{#if settings && settings.length > 0}
|
{#if settings && settings.length > 0}
|
||||||
{#each settings as setting (`${componentInstance._id}-${setting.key}`)}
|
{#each settings as setting}
|
||||||
{#if canRenderControl(setting)}
|
{#if canRenderControl(setting)}
|
||||||
<PropertyControl
|
<PropertyControl
|
||||||
type={setting.type}
|
type={setting.type}
|
||||||
control={getControl(setting.type)}
|
control={getComponentForSettingType(setting.type)}
|
||||||
label={setting.label}
|
label={setting.label}
|
||||||
key={setting.key}
|
key={setting.key}
|
||||||
value={componentInstance[setting.key] ??
|
value={componentInstance[setting.key] ??
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
<script>
|
||||||
|
import { DetailSummary, ActionButton, Drawer, Button } from "@budibase/bbui"
|
||||||
|
import { store } from "builderStore"
|
||||||
|
import ConditionalUIDrawer from "./PropertyControls/ConditionalUIDrawer.svelte"
|
||||||
|
|
||||||
|
export let componentInstance
|
||||||
|
|
||||||
|
let tempValue
|
||||||
|
let drawer
|
||||||
|
|
||||||
|
const openDrawer = () => {
|
||||||
|
tempValue = JSON.parse(JSON.stringify(componentInstance?._conditions ?? []))
|
||||||
|
drawer.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
const save = () => {
|
||||||
|
store.actions.components.updateConditions(tempValue)
|
||||||
|
drawer.hide()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DetailSummary
|
||||||
|
name={`Conditions${componentInstance?._conditions ? " *" : ""}`}
|
||||||
|
collapsible={false}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<ActionButton on:click={openDrawer}>Configure conditions</ActionButton>
|
||||||
|
</div>
|
||||||
|
</DetailSummary>
|
||||||
|
<Drawer bind:this={drawer} title="Conditions">
|
||||||
|
<svelte:fragment slot="description">
|
||||||
|
Show, hide and update components in response to conditions being met.
|
||||||
|
</svelte:fragment>
|
||||||
|
<Button cta slot="buttons" on:click={() => save()}>Save</Button>
|
||||||
|
<ConditionalUIDrawer slot="body" bind:conditions={tempValue} />
|
||||||
|
</Drawer>
|
|
@ -5,6 +5,7 @@
|
||||||
import ComponentSettingsSection from "./ComponentSettingsSection.svelte"
|
import ComponentSettingsSection from "./ComponentSettingsSection.svelte"
|
||||||
import DesignSection from "./DesignSection.svelte"
|
import DesignSection from "./DesignSection.svelte"
|
||||||
import CustomStylesSection from "./CustomStylesSection.svelte"
|
import CustomStylesSection from "./CustomStylesSection.svelte"
|
||||||
|
import ConditionalUISection from "./ConditionalUISection.svelte"
|
||||||
|
|
||||||
$: componentInstance = $selectedComponent
|
$: componentInstance = $selectedComponent
|
||||||
$: componentDefinition = store.actions.components.getDefinition(
|
$: componentDefinition = store.actions.components.getDefinition(
|
||||||
|
@ -15,10 +16,13 @@
|
||||||
<Tabs selected="Settings" noPadding>
|
<Tabs selected="Settings" noPadding>
|
||||||
<Tab title="Settings">
|
<Tab title="Settings">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<ScreenSettingsSection {componentInstance} {componentDefinition} />
|
{#key componentInstance?._id}
|
||||||
<ComponentSettingsSection {componentInstance} {componentDefinition} />
|
<ScreenSettingsSection {componentInstance} {componentDefinition} />
|
||||||
<DesignSection {componentInstance} {componentDefinition} />
|
<ComponentSettingsSection {componentInstance} {componentDefinition} />
|
||||||
<CustomStylesSection {componentInstance} {componentDefinition} />
|
<DesignSection {componentInstance} {componentDefinition} />
|
||||||
|
<CustomStylesSection {componentInstance} {componentDefinition} />
|
||||||
|
<ConditionalUISection {componentInstance} {componentDefinition} />
|
||||||
|
{/key}
|
||||||
</div>
|
</div>
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
|
@ -0,0 +1,290 @@
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Body,
|
||||||
|
Icon,
|
||||||
|
DrawerContent,
|
||||||
|
Layout,
|
||||||
|
Select,
|
||||||
|
DatePicker,
|
||||||
|
} from "@budibase/bbui"
|
||||||
|
import { flip } from "svelte/animate"
|
||||||
|
import { dndzone } from "svelte-dnd-action"
|
||||||
|
import { generate } from "shortid"
|
||||||
|
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
||||||
|
import { OperatorOptions, getValidOperatorsForType } from "helpers/lucene"
|
||||||
|
import { getBindableProperties } from "builderStore/dataBinding"
|
||||||
|
import { currentAsset, selectedComponent, store } from "builderStore"
|
||||||
|
import { getComponentForSettingType } from "./componentSettings"
|
||||||
|
import PropertyControl from "./PropertyControl.svelte"
|
||||||
|
|
||||||
|
export let conditions = []
|
||||||
|
|
||||||
|
const flipDurationMs = 150
|
||||||
|
const actionOptions = [
|
||||||
|
{
|
||||||
|
label: "Hide component",
|
||||||
|
value: "hide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Show component",
|
||||||
|
value: "show",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Update setting",
|
||||||
|
value: "update",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const valueTypeOptions = [
|
||||||
|
{
|
||||||
|
value: "string",
|
||||||
|
label: "Binding",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "number",
|
||||||
|
label: "Number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "datetime",
|
||||||
|
label: "Date",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "boolean",
|
||||||
|
label: "Boolean",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
let dragDisabled = true
|
||||||
|
$: definition = store.actions.components.getDefinition(
|
||||||
|
$selectedComponent?._component
|
||||||
|
)
|
||||||
|
$: settings = (definition?.settings ?? []).map(setting => {
|
||||||
|
return {
|
||||||
|
label: setting.label,
|
||||||
|
value: setting.key,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$: bindableProperties = getBindableProperties(
|
||||||
|
$currentAsset,
|
||||||
|
$store.selectedComponentId
|
||||||
|
)
|
||||||
|
$: conditions.forEach(link => {
|
||||||
|
if (!link.id) {
|
||||||
|
link.id = generate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const getSettingDefinition = key => {
|
||||||
|
return definition?.settings?.find(setting => {
|
||||||
|
return setting.key === key
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getComponentForSetting = key => {
|
||||||
|
const settingDefinition = getSettingDefinition(key)
|
||||||
|
return getComponentForSettingType(settingDefinition?.type || "text")
|
||||||
|
}
|
||||||
|
|
||||||
|
const addCondition = () => {
|
||||||
|
conditions = [
|
||||||
|
...conditions,
|
||||||
|
{
|
||||||
|
valueType: "string",
|
||||||
|
id: generate(),
|
||||||
|
action: "hide",
|
||||||
|
operator: OperatorOptions.Equals.value,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeCondition = id => {
|
||||||
|
conditions = conditions.filter(link => link.id !== id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFinalize = e => {
|
||||||
|
updateConditions(e)
|
||||||
|
dragDisabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateConditions = e => {
|
||||||
|
conditions = e.detail.items
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOperatorOptions = condition => {
|
||||||
|
return getValidOperatorsForType(condition.valueType)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onOperatorChange = (condition, newOperator) => {
|
||||||
|
const noValueOptions = [
|
||||||
|
OperatorOptions.Empty.value,
|
||||||
|
OperatorOptions.NotEmpty.value,
|
||||||
|
]
|
||||||
|
condition.noValue = noValueOptions.includes(newOperator)
|
||||||
|
if (condition.noValue) {
|
||||||
|
condition.referenceValue = null
|
||||||
|
condition.valueType = "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onValueTypeChange = (condition, newType) => {
|
||||||
|
condition.referenceValue = null
|
||||||
|
|
||||||
|
// Ensure a valid operator is set
|
||||||
|
const validOperators = getValidOperatorsForType(newType).map(x => x.value)
|
||||||
|
if (!validOperators.includes(condition.operator)) {
|
||||||
|
condition.operator = validOperators[0] ?? OperatorOptions.Equals.value
|
||||||
|
onOperatorChange(condition, condition.operator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DrawerContent>
|
||||||
|
<div class="container">
|
||||||
|
<Layout noPadding>
|
||||||
|
{#if conditions?.length}
|
||||||
|
<div
|
||||||
|
class="conditions"
|
||||||
|
use:dndzone={{
|
||||||
|
items: conditions,
|
||||||
|
flipDurationMs,
|
||||||
|
dropTargetStyle: { outline: "none" },
|
||||||
|
dragDisabled,
|
||||||
|
}}
|
||||||
|
on:finalize={handleFinalize}
|
||||||
|
on:consider={updateConditions}
|
||||||
|
>
|
||||||
|
{#each conditions as condition (condition.id)}
|
||||||
|
<div
|
||||||
|
class="condition"
|
||||||
|
class:update={condition.action === "update"}
|
||||||
|
animate:flip={{ duration: flipDurationMs }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="handle"
|
||||||
|
aria-label="drag-handle"
|
||||||
|
style={dragDisabled ? "cursor: grab" : "cursor: grabbing"}
|
||||||
|
on:mousedown={() => (dragDisabled = false)}
|
||||||
|
>
|
||||||
|
<Icon name="DragHandle" size="XL" />
|
||||||
|
</div>
|
||||||
|
<Select
|
||||||
|
placeholder={null}
|
||||||
|
options={actionOptions}
|
||||||
|
bind:value={condition.action}
|
||||||
|
/>
|
||||||
|
{#if condition.action === "update"}
|
||||||
|
<Select options={settings} bind:value={condition.setting} />
|
||||||
|
<div>TO</div>
|
||||||
|
{#if getSettingDefinition(condition.setting)}
|
||||||
|
<PropertyControl
|
||||||
|
type={getSettingDefinition(condition.setting).type}
|
||||||
|
control={getComponentForSetting(condition.setting)}
|
||||||
|
key={getSettingDefinition(condition.setting).key}
|
||||||
|
value={condition.settingValue}
|
||||||
|
componentInstance={$selectedComponent}
|
||||||
|
onChange={val => (condition.settingValue = val)}
|
||||||
|
props={{
|
||||||
|
options: getSettingDefinition(condition.setting).options,
|
||||||
|
placeholder: getSettingDefinition(condition.setting)
|
||||||
|
.placeholder,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<Select disabled placeholder=" " />
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
<div>IF</div>
|
||||||
|
<DrawerBindableInput
|
||||||
|
bindings={bindableProperties}
|
||||||
|
placeholder="Value"
|
||||||
|
value={condition.newValue}
|
||||||
|
on:change={e => (condition.newValue = e.detail)}
|
||||||
|
/>
|
||||||
|
<Select
|
||||||
|
placeholder={null}
|
||||||
|
options={getOperatorOptions(condition)}
|
||||||
|
bind:value={condition.operator}
|
||||||
|
on:change={e => onOperatorChange(condition, e.detail)}
|
||||||
|
/>
|
||||||
|
<Select
|
||||||
|
disabled={condition.noValue}
|
||||||
|
options={valueTypeOptions}
|
||||||
|
bind:value={condition.valueType}
|
||||||
|
placeholder={null}
|
||||||
|
on:change={e => onValueTypeChange(condition, e.detail)}
|
||||||
|
/>
|
||||||
|
{#if ["string", "number"].includes(condition.valueType)}
|
||||||
|
<DrawerBindableInput
|
||||||
|
disabled={condition.noValue}
|
||||||
|
bindings={bindableProperties}
|
||||||
|
placeholder="Value"
|
||||||
|
value={condition.referenceValue}
|
||||||
|
on:change={e => (condition.referenceValue = e.detail)}
|
||||||
|
/>
|
||||||
|
{:else if condition.valueType === "datetime"}
|
||||||
|
<DatePicker
|
||||||
|
placeholder="Value"
|
||||||
|
disabled={condition.noValue}
|
||||||
|
bind:value={condition.referenceValue}
|
||||||
|
/>
|
||||||
|
{:else if condition.valueType === "boolean"}
|
||||||
|
<Select
|
||||||
|
placeholder="Value"
|
||||||
|
disabled={condition.noValue}
|
||||||
|
options={["True", "False"]}
|
||||||
|
bind:value={condition.referenceValue}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<Icon
|
||||||
|
name="Close"
|
||||||
|
hoverable
|
||||||
|
size="S"
|
||||||
|
on:click={() => removeCondition(condition.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<Body size="S">Add your first condition to get started.</Body>
|
||||||
|
{/if}
|
||||||
|
<div>
|
||||||
|
<Button secondary icon="Add" on:click={addCondition}>
|
||||||
|
Add condition
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
</div>
|
||||||
|
</DrawerContent>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.conditions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
.condition {
|
||||||
|
gap: var(--spacing-l);
|
||||||
|
display: grid;
|
||||||
|
align-items: center;
|
||||||
|
grid-template-columns: auto 1fr auto 1fr 1fr 1fr 1fr auto;
|
||||||
|
border-radius: var(--border-radius-s);
|
||||||
|
transition: background-color ease-in-out 130ms;
|
||||||
|
}
|
||||||
|
.condition.update {
|
||||||
|
grid-template-columns: auto 1fr 1fr auto 1fr auto 1fr 1fr 1fr 1fr auto;
|
||||||
|
}
|
||||||
|
.condition:hover {
|
||||||
|
background-color: var(--spectrum-global-color-gray-100);
|
||||||
|
}
|
||||||
|
.handle {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -11,44 +11,11 @@
|
||||||
import { getBindableProperties } from "builderStore/dataBinding"
|
import { getBindableProperties } from "builderStore/dataBinding"
|
||||||
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
||||||
import { generate } from "shortid"
|
import { generate } from "shortid"
|
||||||
|
import { OperatorOptions, getValidOperatorsForType } from "helpers/lucene"
|
||||||
|
|
||||||
export let schemaFields
|
export let schemaFields
|
||||||
export let value
|
export let value
|
||||||
|
|
||||||
const OperatorOptions = {
|
|
||||||
Equals: {
|
|
||||||
value: "equal",
|
|
||||||
label: "Equals",
|
|
||||||
},
|
|
||||||
NotEquals: {
|
|
||||||
value: "notEqual",
|
|
||||||
label: "Not equals",
|
|
||||||
},
|
|
||||||
Empty: {
|
|
||||||
value: "empty",
|
|
||||||
label: "Is empty",
|
|
||||||
},
|
|
||||||
NotEmpty: {
|
|
||||||
value: "notEmpty",
|
|
||||||
label: "Is not empty",
|
|
||||||
},
|
|
||||||
StartsWith: {
|
|
||||||
value: "string",
|
|
||||||
label: "Starts with",
|
|
||||||
},
|
|
||||||
Like: {
|
|
||||||
value: "fuzzy",
|
|
||||||
label: "Like",
|
|
||||||
},
|
|
||||||
MoreThan: {
|
|
||||||
value: "rangeLow",
|
|
||||||
label: "More than",
|
|
||||||
},
|
|
||||||
LessThan: {
|
|
||||||
value: "rangeHigh",
|
|
||||||
label: "Less than",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const BannedTypes = ["link", "attachment"]
|
const BannedTypes = ["link", "attachment"]
|
||||||
$: bindableProperties = getBindableProperties(
|
$: bindableProperties = getBindableProperties(
|
||||||
$currentAsset,
|
$currentAsset,
|
||||||
|
@ -75,61 +42,16 @@
|
||||||
value = value.filter(field => field.id !== id)
|
value = value.filter(field => field.id !== id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getValidOperatorsForType = type => {
|
|
||||||
const Op = OperatorOptions
|
|
||||||
if (type === "string") {
|
|
||||||
return [
|
|
||||||
Op.Equals,
|
|
||||||
Op.NotEquals,
|
|
||||||
Op.StartsWith,
|
|
||||||
Op.Like,
|
|
||||||
Op.Empty,
|
|
||||||
Op.NotEmpty,
|
|
||||||
]
|
|
||||||
} else if (type === "number") {
|
|
||||||
return [
|
|
||||||
Op.Equals,
|
|
||||||
Op.NotEquals,
|
|
||||||
Op.MoreThan,
|
|
||||||
Op.LessThan,
|
|
||||||
Op.Empty,
|
|
||||||
Op.NotEmpty,
|
|
||||||
]
|
|
||||||
} else if (type === "options") {
|
|
||||||
return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty]
|
|
||||||
} else if (type === "boolean") {
|
|
||||||
return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty]
|
|
||||||
} else if (type === "longform") {
|
|
||||||
return [
|
|
||||||
Op.Equals,
|
|
||||||
Op.NotEquals,
|
|
||||||
Op.StartsWith,
|
|
||||||
Op.Like,
|
|
||||||
Op.Empty,
|
|
||||||
Op.NotEmpty,
|
|
||||||
]
|
|
||||||
} else if (type === "datetime") {
|
|
||||||
return [
|
|
||||||
Op.Equals,
|
|
||||||
Op.NotEquals,
|
|
||||||
Op.MoreThan,
|
|
||||||
Op.LessThan,
|
|
||||||
Op.Empty,
|
|
||||||
Op.NotEmpty,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const onFieldChange = (expression, field) => {
|
const onFieldChange = (expression, field) => {
|
||||||
// Update the field type
|
// Update the field type
|
||||||
expression.type = schemaFields.find(x => x.name === field)?.type
|
expression.type = schemaFields.find(x => x.name === field)?.type
|
||||||
|
|
||||||
// Ensure a valid operator is set
|
// Ensure a valid operator is set
|
||||||
const validOperators = getValidOperatorsForType(expression.type)
|
const validOperators = getValidOperatorsForType(expression.type).map(
|
||||||
|
x => x.value
|
||||||
|
)
|
||||||
if (!validOperators.includes(expression.operator)) {
|
if (!validOperators.includes(expression.operator)) {
|
||||||
expression.operator =
|
expression.operator = validOperators[0] ?? OperatorOptions.Equals.value
|
||||||
validOperators[0]?.value ?? OperatorOptions.Equals.value
|
|
||||||
onOperatorChange(expression, expression.operator)
|
onOperatorChange(expression, expression.operator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,13 @@
|
||||||
export let links = []
|
export let links = []
|
||||||
|
|
||||||
const flipDurationMs = 150
|
const flipDurationMs = 150
|
||||||
|
let dragDisabled = true
|
||||||
|
|
||||||
$: links.forEach(link => {
|
$: links.forEach(link => {
|
||||||
if (!link.id) {
|
if (!link.id) {
|
||||||
link.id = generate()
|
link.id = generate()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
$: urlOptions = $store.screens
|
$: urlOptions = $store.screens
|
||||||
.map(screen => screen.routing?.route)
|
.map(screen => screen.routing?.route)
|
||||||
.filter(x => x != null)
|
.filter(x => x != null)
|
||||||
|
@ -37,6 +37,11 @@
|
||||||
const updateLinks = e => {
|
const updateLinks = e => {
|
||||||
links = e.detail.items
|
links = e.detail.items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleFinalize = e => {
|
||||||
|
updateLinks(e)
|
||||||
|
dragDisabled = true
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<DrawerContent>
|
<DrawerContent>
|
||||||
|
@ -49,13 +54,21 @@
|
||||||
items: links,
|
items: links,
|
||||||
flipDurationMs,
|
flipDurationMs,
|
||||||
dropTargetStyle: { outline: "none" },
|
dropTargetStyle: { outline: "none" },
|
||||||
|
dragDisabled,
|
||||||
}}
|
}}
|
||||||
on:finalize={updateLinks}
|
on:finalize={handleFinalize}
|
||||||
on:consider={updateLinks}
|
on:consider={updateLinks}
|
||||||
>
|
>
|
||||||
{#each links as link (link.id)}
|
{#each links as link (link.id)}
|
||||||
<div class="link" animate:flip={{ duration: flipDurationMs }}>
|
<div class="link" animate:flip={{ duration: flipDurationMs }}>
|
||||||
<Icon name="DragHandle" size="XL" />
|
<div
|
||||||
|
class="handle"
|
||||||
|
aria-label="drag-handle"
|
||||||
|
style={dragDisabled ? "cursor: grab" : "cursor: grabbing"}
|
||||||
|
on:mousedown={() => (dragDisabled = false)}
|
||||||
|
>
|
||||||
|
<Icon name="DragHandle" size="XL" />
|
||||||
|
</div>
|
||||||
<Input bind:value={link.text} placeholder="Text" />
|
<Input bind:value={link.text} placeholder="Text" />
|
||||||
<Combobox
|
<Combobox
|
||||||
bind:value={link.url}
|
bind:value={link.url}
|
||||||
|
@ -90,7 +103,7 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
gap: var(--spacing-s);
|
gap: var(--spacing-m);
|
||||||
}
|
}
|
||||||
.link {
|
.link {
|
||||||
gap: var(--spacing-l);
|
gap: var(--spacing-l);
|
||||||
|
@ -108,4 +121,8 @@
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
.handle {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -70,7 +70,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="property-control" bind:this={anchor} data-cy={`setting-${key}`}>
|
<div class="property-control" bind:this={anchor} data-cy={`setting-${key}`}>
|
||||||
{#if type !== "boolean"}
|
{#if type !== "boolean" && label}
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<Label>{label}</Label>
|
<Label>{label}</Label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { Checkbox, Input, Select } from "@budibase/bbui"
|
||||||
|
import DataSourceSelect from "./DataSourceSelect.svelte"
|
||||||
|
import DataProviderSelect from "./DataProviderSelect.svelte"
|
||||||
|
import EventsEditor from "./EventsEditor"
|
||||||
|
import TableSelect from "./TableSelect.svelte"
|
||||||
|
import ColorPicker from "./ColorPicker.svelte"
|
||||||
|
import { IconSelect } from "./IconSelect"
|
||||||
|
import FieldSelect from "./FieldSelect.svelte"
|
||||||
|
import MultiFieldSelect from "./MultiFieldSelect.svelte"
|
||||||
|
import SchemaSelect from "./SchemaSelect.svelte"
|
||||||
|
import SectionSelect from "./SectionSelect.svelte"
|
||||||
|
import NavigationEditor from "./NavigationEditor/NavigationEditor.svelte"
|
||||||
|
import FilterEditor from "./FilterEditor/FilterEditor.svelte"
|
||||||
|
import URLSelect from "./URLSelect.svelte"
|
||||||
|
import StringFieldSelect from "./StringFieldSelect.svelte"
|
||||||
|
import NumberFieldSelect from "./NumberFieldSelect.svelte"
|
||||||
|
import OptionsFieldSelect from "./OptionsFieldSelect.svelte"
|
||||||
|
import BooleanFieldSelect from "./BooleanFieldSelect.svelte"
|
||||||
|
import LongFormFieldSelect from "./LongFormFieldSelect.svelte"
|
||||||
|
import DateTimeFieldSelect from "./DateTimeFieldSelect.svelte"
|
||||||
|
import AttachmentFieldSelect from "./AttachmentFieldSelect.svelte"
|
||||||
|
import RelationshipFieldSelect from "./RelationshipFieldSelect.svelte"
|
||||||
|
|
||||||
|
const componentMap = {
|
||||||
|
text: Input,
|
||||||
|
select: Select,
|
||||||
|
dataSource: DataSourceSelect,
|
||||||
|
dataProvider: DataProviderSelect,
|
||||||
|
boolean: Checkbox,
|
||||||
|
number: Input,
|
||||||
|
event: EventsEditor,
|
||||||
|
table: TableSelect,
|
||||||
|
color: ColorPicker,
|
||||||
|
icon: IconSelect,
|
||||||
|
field: FieldSelect,
|
||||||
|
multifield: MultiFieldSelect,
|
||||||
|
schema: SchemaSelect,
|
||||||
|
section: SectionSelect,
|
||||||
|
navigation: NavigationEditor,
|
||||||
|
filter: FilterEditor,
|
||||||
|
url: URLSelect,
|
||||||
|
"field/string": StringFieldSelect,
|
||||||
|
"field/number": NumberFieldSelect,
|
||||||
|
"field/options": OptionsFieldSelect,
|
||||||
|
"field/boolean": BooleanFieldSelect,
|
||||||
|
"field/longform": LongFormFieldSelect,
|
||||||
|
"field/datetime": DateTimeFieldSelect,
|
||||||
|
"field/attachment": AttachmentFieldSelect,
|
||||||
|
"field/link": RelationshipFieldSelect,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getComponentForSettingType = type => {
|
||||||
|
return componentMap[type]
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
export const OperatorOptions = {
|
||||||
|
Equals: {
|
||||||
|
value: "equal",
|
||||||
|
label: "Equals",
|
||||||
|
},
|
||||||
|
NotEquals: {
|
||||||
|
value: "notEqual",
|
||||||
|
label: "Not equals",
|
||||||
|
},
|
||||||
|
Empty: {
|
||||||
|
value: "empty",
|
||||||
|
label: "Is empty",
|
||||||
|
},
|
||||||
|
NotEmpty: {
|
||||||
|
value: "notEmpty",
|
||||||
|
label: "Is not empty",
|
||||||
|
},
|
||||||
|
StartsWith: {
|
||||||
|
value: "string",
|
||||||
|
label: "Starts with",
|
||||||
|
},
|
||||||
|
Like: {
|
||||||
|
value: "fuzzy",
|
||||||
|
label: "Like",
|
||||||
|
},
|
||||||
|
MoreThan: {
|
||||||
|
value: "rangeLow",
|
||||||
|
label: "More than",
|
||||||
|
},
|
||||||
|
LessThan: {
|
||||||
|
value: "rangeHigh",
|
||||||
|
label: "Less than",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getValidOperatorsForType = type => {
|
||||||
|
const Op = OperatorOptions
|
||||||
|
if (type === "string") {
|
||||||
|
return [
|
||||||
|
Op.Equals,
|
||||||
|
Op.NotEquals,
|
||||||
|
Op.StartsWith,
|
||||||
|
Op.Like,
|
||||||
|
Op.Empty,
|
||||||
|
Op.NotEmpty,
|
||||||
|
]
|
||||||
|
} else if (type === "number") {
|
||||||
|
return [
|
||||||
|
Op.Equals,
|
||||||
|
Op.NotEquals,
|
||||||
|
Op.MoreThan,
|
||||||
|
Op.LessThan,
|
||||||
|
Op.Empty,
|
||||||
|
Op.NotEmpty,
|
||||||
|
]
|
||||||
|
} else if (type === "options") {
|
||||||
|
return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty]
|
||||||
|
} else if (type === "boolean") {
|
||||||
|
return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty]
|
||||||
|
} else if (type === "longform") {
|
||||||
|
return [
|
||||||
|
Op.Equals,
|
||||||
|
Op.NotEquals,
|
||||||
|
Op.StartsWith,
|
||||||
|
Op.Like,
|
||||||
|
Op.Empty,
|
||||||
|
Op.NotEmpty,
|
||||||
|
]
|
||||||
|
} else if (type === "datetime") {
|
||||||
|
return [
|
||||||
|
Op.Equals,
|
||||||
|
Op.NotEquals,
|
||||||
|
Op.MoreThan,
|
||||||
|
Op.LessThan,
|
||||||
|
Op.Empty,
|
||||||
|
Op.NotEmpty,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
|
@ -58,5 +58,6 @@
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
gap: var(--spacing-l);
|
gap: var(--spacing-l);
|
||||||
background-color: var(--background);
|
background-color: var(--background);
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
import { fetchData } from "helpers"
|
import { fetchData } from "helpers"
|
||||||
import { users, auth } from "stores/portal"
|
import { users, auth } from "stores/portal"
|
||||||
|
|
||||||
import TagsRenderer from "./_components/TagsTableRenderer.svelte"
|
import TagsRenderer from "./_components/RolesTagsTableRenderer.svelte"
|
||||||
|
|
||||||
import UpdateRolesModal from "./_components/UpdateRolesModal.svelte"
|
import UpdateRolesModal from "./_components/UpdateRolesModal.svelte"
|
||||||
import ForceResetPasswordModal from "./_components/ForceResetPasswordModal.svelte"
|
import ForceResetPasswordModal from "./_components/ForceResetPasswordModal.svelte"
|
||||||
|
|
||||||
|
@ -36,7 +37,8 @@
|
||||||
$: defaultRoleId = $userFetch?.data?.builder?.global ? "ADMIN" : "BASIC"
|
$: defaultRoleId = $userFetch?.data?.builder?.global ? "ADMIN" : "BASIC"
|
||||||
// Merge the Apps list and the roles response to get something that makes sense for the table
|
// Merge the Apps list and the roles response to get something that makes sense for the table
|
||||||
$: appList = Object.keys($apps?.data).map(id => {
|
$: appList = Object.keys($apps?.data).map(id => {
|
||||||
const role = $userFetch?.data?.roles?.[id] || defaultRoleId
|
const roleId = $userFetch?.data?.roles?.[id] || defaultRoleId
|
||||||
|
const role = $apps?.data?.[id].roles.find(role => role._id === roleId)
|
||||||
return {
|
return {
|
||||||
...$apps?.data?.[id],
|
...$apps?.data?.[id],
|
||||||
_id: id,
|
_id: id,
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<script>
|
||||||
|
import TagsTableRenderer from "./TagsTableRenderer.svelte"
|
||||||
|
export let value
|
||||||
|
|
||||||
|
$: roles = value?.filter(role => role != null).map(role => role.name) ?? []
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<TagsTableRenderer value={roles} />
|
|
@ -4,9 +4,9 @@
|
||||||
|
|
||||||
const displayLimit = 5
|
const displayLimit = 5
|
||||||
|
|
||||||
$: roles = value?.filter(role => role != null) ?? []
|
$: values = value?.filter(value => value != null) ?? []
|
||||||
$: tags = roles.slice(0, displayLimit)
|
$: tags = values.slice(0, displayLimit)
|
||||||
$: leftover = roles.length - tags.length
|
$: leftover = values.length - tags.length
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="tag-renderer">
|
<div class="tag-renderer">
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
|
|
||||||
const roles = app.roles
|
const roles = app.roles
|
||||||
let options = roles
|
let options = roles
|
||||||
|
.filter(role => role._id !== "PUBLIC")
|
||||||
.map(role => ({ value: role._id, label: role.name }))
|
.map(role => ({ value: role._id, label: role.name }))
|
||||||
.filter(role => role.value !== "PUBLIC")
|
|
||||||
let selectedRole = user?.roles?.[app?._id]
|
let selectedRole = user?.roles?.[app?._id]
|
||||||
|
|
||||||
async function updateUserRoles() {
|
async function updateUserRoles() {
|
||||||
|
@ -48,5 +48,7 @@
|
||||||
on:change
|
on:change
|
||||||
{options}
|
{options}
|
||||||
label="Role"
|
label="Role"
|
||||||
|
getOptionLabel={role => role.name}
|
||||||
|
getOptionValue={role => role._id}
|
||||||
/>
|
/>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/cli",
|
"name": "@budibase/cli",
|
||||||
"version": "0.9.79-alpha.4",
|
"version": "0.9.80-alpha.9",
|
||||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "0.9.79-alpha.4",
|
"version": "0.9.80-alpha.9",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"module": "dist/budibase-client.js",
|
"module": "dist/budibase-client.js",
|
||||||
"main": "dist/budibase-client.js",
|
"main": "dist/budibase-client.js",
|
||||||
|
@ -18,9 +18,9 @@
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^0.9.79-alpha.4",
|
"@budibase/bbui": "^0.9.80-alpha.9",
|
||||||
"@budibase/standard-components": "^0.9.79-alpha.4",
|
"@budibase/standard-components": "^0.9.80-alpha.9",
|
||||||
"@budibase/string-templates": "^0.9.79-alpha.4",
|
"@budibase/string-templates": "^0.9.80-alpha.9",
|
||||||
"regexparam": "^1.3.0",
|
"regexparam": "^1.3.0",
|
||||||
"shortid": "^2.2.15",
|
"shortid": "^2.2.15",
|
||||||
"svelte-spa-router": "^3.0.5"
|
"svelte-spa-router": "^3.0.5"
|
||||||
|
|
|
@ -8,11 +8,18 @@
|
||||||
import { hashString } from "../utils/hash"
|
import { hashString } from "../utils/hash"
|
||||||
import Manifest from "@budibase/standard-components/manifest.json"
|
import Manifest from "@budibase/standard-components/manifest.json"
|
||||||
import { Placeholder } from "@budibase/standard-components"
|
import { Placeholder } from "@budibase/standard-components"
|
||||||
|
import {
|
||||||
|
getActiveConditions,
|
||||||
|
reduceConditionActions,
|
||||||
|
} from "../utils/conditions"
|
||||||
|
|
||||||
export let instance = {}
|
export let instance = {}
|
||||||
|
|
||||||
// Props that will be passed to the component instance
|
// The enriched component settings
|
||||||
let componentProps
|
let enrichedSettings
|
||||||
|
|
||||||
|
// Any prop overrides that need to be applied due to conditional UI
|
||||||
|
let conditionalSettings
|
||||||
|
|
||||||
// Props are hashed when inside the builder preview and used as a key, so that
|
// Props are hashed when inside the builder preview and used as a key, so that
|
||||||
// components fully remount whenever any props change
|
// components fully remount whenever any props change
|
||||||
|
@ -28,6 +35,9 @@
|
||||||
let lastContextKey
|
let lastContextKey
|
||||||
let lastInstanceKey
|
let lastInstanceKey
|
||||||
|
|
||||||
|
// Visibility flag used by conditional UI
|
||||||
|
let visible = true
|
||||||
|
|
||||||
// Get contexts
|
// Get contexts
|
||||||
const context = getContext("context")
|
const context = getContext("context")
|
||||||
const insideScreenslot = !!getContext("screenslot")
|
const insideScreenslot = !!getContext("screenslot")
|
||||||
|
@ -54,6 +64,8 @@
|
||||||
$builderStore.inBuilder &&
|
$builderStore.inBuilder &&
|
||||||
$builderStore.selectedComponentId === instance._id
|
$builderStore.selectedComponentId === instance._id
|
||||||
$: interactive = $builderStore.previewType === "layout" || insideScreenslot
|
$: interactive = $builderStore.previewType === "layout" || insideScreenslot
|
||||||
|
$: evaluateConditions(enrichedSettings?._conditions)
|
||||||
|
$: componentSettings = { ...enrichedSettings, ...conditionalSettings }
|
||||||
|
|
||||||
// Update component context
|
// Update component context
|
||||||
$: componentStore.set({
|
$: componentStore.set({
|
||||||
|
@ -62,14 +74,14 @@
|
||||||
styles: { ...instance._styles, id, empty, interactive },
|
styles: { ...instance._styles, id, empty, interactive },
|
||||||
empty,
|
empty,
|
||||||
selected,
|
selected,
|
||||||
props: componentProps,
|
props: componentSettings,
|
||||||
name,
|
name,
|
||||||
})
|
})
|
||||||
|
|
||||||
const getRawProps = instance => {
|
const getRawProps = instance => {
|
||||||
let validProps = {}
|
let validProps = {}
|
||||||
Object.entries(instance)
|
Object.entries(instance)
|
||||||
.filter(([name]) => !name.startsWith("_"))
|
.filter(([name]) => name === "_conditions" || !name.startsWith("_"))
|
||||||
.forEach(([key, value]) => {
|
.forEach(([key, value]) => {
|
||||||
validProps[key] = value
|
validProps[key] = value
|
||||||
})
|
})
|
||||||
|
@ -123,34 +135,55 @@
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let propsChanged = false
|
let propsChanged = false
|
||||||
if (!componentProps) {
|
if (!enrichedSettings) {
|
||||||
componentProps = {}
|
enrichedSettings = {}
|
||||||
propsChanged = true
|
propsChanged = true
|
||||||
}
|
}
|
||||||
Object.keys(enrichedProps).forEach(key => {
|
Object.keys(enrichedProps).forEach(key => {
|
||||||
if (!propsAreSame(enrichedProps[key], componentProps[key])) {
|
if (!propsAreSame(enrichedProps[key], enrichedSettings[key])) {
|
||||||
propsChanged = true
|
propsChanged = true
|
||||||
componentProps[key] = enrichedProps[key]
|
enrichedSettings[key] = enrichedProps[key]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Update the hash if we're in the builder so we can fully remount this
|
// Update the hash if we're in the builder so we can fully remount this
|
||||||
// component
|
// component
|
||||||
if (get(builderStore).inBuilder && propsChanged) {
|
if (get(builderStore).inBuilder && propsChanged) {
|
||||||
propsHash = hashString(JSON.stringify(componentProps))
|
propsHash = hashString(JSON.stringify(enrichedSettings))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const evaluateConditions = conditions => {
|
||||||
|
if (!conditions?.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default visible to false if there is a show condition
|
||||||
|
let nextVisible = !conditions.find(condition => condition.action === "show")
|
||||||
|
|
||||||
|
// Execute conditions and determine settings and visibility changes
|
||||||
|
const activeConditions = getActiveConditions(conditions)
|
||||||
|
const result = reduceConditionActions(activeConditions)
|
||||||
|
if (result.visible != null) {
|
||||||
|
nextVisible = result.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update state from condition results
|
||||||
|
conditionalSettings = result.settingUpdates
|
||||||
|
visible = nextVisible
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
{#key propsHash}
|
||||||
class={`component ${id}`}
|
{#if constructor && componentSettings && visible}
|
||||||
data-type={interactive ? "component" : ""}
|
<div
|
||||||
data-id={id}
|
class={`component ${id}`}
|
||||||
data-name={name}
|
data-type={interactive ? "component" : ""}
|
||||||
>
|
data-id={id}
|
||||||
{#key propsHash}
|
data-name={name}
|
||||||
{#if constructor && componentProps}
|
class:hidden={!visible}
|
||||||
<svelte:component this={constructor} {...componentProps}>
|
>
|
||||||
|
<svelte:component this={constructor} {...componentSettings}>
|
||||||
{#if children.length}
|
{#if children.length}
|
||||||
{#each children as child (child._id)}
|
{#each children as child (child._id)}
|
||||||
<svelte:self instance={child} />
|
<svelte:self instance={child} />
|
||||||
|
@ -159,9 +192,9 @@
|
||||||
<Placeholder />
|
<Placeholder />
|
||||||
{/if}
|
{/if}
|
||||||
</svelte:component>
|
</svelte:component>
|
||||||
{/if}
|
</div>
|
||||||
{/key}
|
{/if}
|
||||||
</div>
|
{/key}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.component {
|
.component {
|
||||||
|
|
|
@ -6,8 +6,15 @@ const createAppStore = () => {
|
||||||
|
|
||||||
// Fetches the app definition including screens, layouts and theme
|
// Fetches the app definition including screens, layouts and theme
|
||||||
const fetchAppDefinition = async () => {
|
const fetchAppDefinition = async () => {
|
||||||
const appDefinition = await API.fetchAppPackage(get(store).appId)
|
const appId = get(store)?.appId
|
||||||
store.set(appDefinition)
|
if (!appId) {
|
||||||
|
throw "Cannot fetch app definition without app ID set"
|
||||||
|
}
|
||||||
|
const appDefinition = await API.fetchAppPackage(appId)
|
||||||
|
store.set({
|
||||||
|
...appDefinition,
|
||||||
|
appId: appDefinition?.application?.appId,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the initial app ID
|
// Sets the initial app ID
|
||||||
|
|
|
@ -10,9 +10,14 @@ const createAuthStore = () => {
|
||||||
store.set(user)
|
store.set(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const logOut = async () => {
|
||||||
|
window.document.cookie = `budibase:auth=; budibase:currentapp=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`
|
||||||
|
window.location = "/builder/auth/login"
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe: store.subscribe,
|
subscribe: store.subscribe,
|
||||||
actions: { fetchUser },
|
actions: { fetchUser, logOut },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import { routeStore, builderStore, confirmationStore } from "../store"
|
import {
|
||||||
|
routeStore,
|
||||||
|
builderStore,
|
||||||
|
confirmationStore,
|
||||||
|
authStore,
|
||||||
|
} from "../store"
|
||||||
import { saveRow, deleteRow, executeQuery, triggerAutomation } from "../api"
|
import { saveRow, deleteRow, executeQuery, triggerAutomation } from "../api"
|
||||||
import { ActionTypes } from "../constants"
|
import { ActionTypes } from "../constants"
|
||||||
|
|
||||||
const saveRowHandler = async (action, context) => {
|
const saveRowHandler = async (action, context) => {
|
||||||
const { fields, providerId } = action.parameters
|
const { fields, providerId, tableId } = action.parameters
|
||||||
if (providerId) {
|
if (providerId) {
|
||||||
let draft = context[providerId]
|
let draft = context[providerId]
|
||||||
if (fields) {
|
if (fields) {
|
||||||
|
@ -12,6 +17,9 @@ const saveRowHandler = async (action, context) => {
|
||||||
draft[field] = value
|
draft[field] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (tableId) {
|
||||||
|
draft.tableId = tableId
|
||||||
|
}
|
||||||
await saveRow(draft)
|
await saveRow(draft)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,6 +82,10 @@ const refreshDatasourceHandler = async (action, context) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const logoutHandler = async () => {
|
||||||
|
await authStore.actions.logOut()
|
||||||
|
}
|
||||||
|
|
||||||
const handlerMap = {
|
const handlerMap = {
|
||||||
["Save Row"]: saveRowHandler,
|
["Save Row"]: saveRowHandler,
|
||||||
["Delete Row"]: deleteRowHandler,
|
["Delete Row"]: deleteRowHandler,
|
||||||
|
@ -82,6 +94,7 @@ const handlerMap = {
|
||||||
["Trigger Automation"]: triggerAutomationHandler,
|
["Trigger Automation"]: triggerAutomationHandler,
|
||||||
["Validate Form"]: validateFormHandler,
|
["Validate Form"]: validateFormHandler,
|
||||||
["Refresh Datasource"]: refreshDatasourceHandler,
|
["Refresh Datasource"]: refreshDatasourceHandler,
|
||||||
|
["Log Out"]: logoutHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmTextMap = {
|
const confirmTextMap = {
|
||||||
|
|
|
@ -43,5 +43,17 @@ export const enrichProps = (props, context) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enrich any click actions in conditions
|
||||||
|
if (enrichedProps._conditions) {
|
||||||
|
enrichedProps._conditions.forEach(condition => {
|
||||||
|
if (condition.setting === "onClick") {
|
||||||
|
condition.settingValue = enrichButtonActions(
|
||||||
|
condition.settingValue,
|
||||||
|
totalContext
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return enrichedProps
|
return enrichedProps
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import {
|
||||||
|
buildLuceneQuery,
|
||||||
|
luceneQuery,
|
||||||
|
} from "../../../standard-components/src/lucene"
|
||||||
|
|
||||||
|
export const getActiveConditions = conditions => {
|
||||||
|
if (!conditions?.length) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return conditions.filter(condition => {
|
||||||
|
// Parse values into correct types
|
||||||
|
if (condition.valueType === "number") {
|
||||||
|
condition.referenceValue = parseFloat(condition.referenceValue)
|
||||||
|
condition.newValue = parseFloat(condition.newValue)
|
||||||
|
} else if (condition.valueType === "datetime") {
|
||||||
|
if (condition.referenceValue) {
|
||||||
|
condition.referenceValue = new Date(
|
||||||
|
condition.referenceValue
|
||||||
|
).toISOString()
|
||||||
|
}
|
||||||
|
if (condition.newValue) {
|
||||||
|
condition.newValue = new Date(condition.newValue).toISOString()
|
||||||
|
}
|
||||||
|
} else if (condition.valueType === "boolean") {
|
||||||
|
condition.referenceValue =
|
||||||
|
`${condition.referenceValue}`.toLowerCase() === "true"
|
||||||
|
condition.newValue = `${condition.newValue}`.toLowerCase() === "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build lucene compatible condition expression
|
||||||
|
const luceneCondition = {
|
||||||
|
...condition,
|
||||||
|
type: condition.valueType,
|
||||||
|
field: "newValue",
|
||||||
|
value: condition.referenceValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = buildLuceneQuery([luceneCondition])
|
||||||
|
const result = luceneQuery([luceneCondition], query)
|
||||||
|
return result.length > 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const reduceConditionActions = conditions => {
|
||||||
|
let settingUpdates = {}
|
||||||
|
let visible = null
|
||||||
|
|
||||||
|
conditions?.forEach(condition => {
|
||||||
|
if (condition.action === "show") {
|
||||||
|
visible = true
|
||||||
|
} else if (condition.action === "hide") {
|
||||||
|
visible = false
|
||||||
|
} else if (condition.setting) {
|
||||||
|
settingUpdates[condition.setting] = condition.settingValue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return { settingUpdates, visible }
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/server",
|
"name": "@budibase/server",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "0.9.79-alpha.4",
|
"version": "0.9.80-alpha.9",
|
||||||
"description": "Budibase Web Server",
|
"description": "Budibase Web Server",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
"url": "https://github.com/Budibase/budibase.git"
|
"url": "https://github.com/Budibase/budibase.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rm -rf dist/ && tsc && mv dist/src/* dist/ && rmdir dist/src/ && yarn postbuild",
|
"build": "rimraf dist/ && tsc && mv dist/src/* dist/ && rmdir dist/src/ && yarn postbuild",
|
||||||
"postbuild": "copyfiles -u 1 src/**/*.svelte dist/ && copyfiles -u 1 src/**/*.hbs dist/ && copyfiles -u 1 src/**/*.json dist/",
|
"postbuild": "copyfiles -u 1 src/**/*.svelte dist/ && copyfiles -u 1 src/**/*.hbs dist/ && copyfiles -u 1 src/**/*.json dist/",
|
||||||
"test": "jest --coverage --maxWorkers=2",
|
"test": "jest --coverage --maxWorkers=2",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
|
@ -62,9 +62,9 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/auth": "^0.9.79-alpha.4",
|
"@budibase/auth": "^0.9.80-alpha.9",
|
||||||
"@budibase/client": "^0.9.79-alpha.4",
|
"@budibase/client": "^0.9.80-alpha.9",
|
||||||
"@budibase/string-templates": "^0.9.79-alpha.4",
|
"@budibase/string-templates": "^0.9.80-alpha.9",
|
||||||
"@elastic/elasticsearch": "7.10.0",
|
"@elastic/elasticsearch": "7.10.0",
|
||||||
"@koa/router": "8.0.0",
|
"@koa/router": "8.0.0",
|
||||||
"@sendgrid/mail": "7.1.1",
|
"@sendgrid/mail": "7.1.1",
|
||||||
|
@ -117,7 +117,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.14.3",
|
"@babel/core": "^7.14.3",
|
||||||
"@babel/preset-env": "^7.14.4",
|
"@babel/preset-env": "^7.14.4",
|
||||||
"@budibase/standard-components": "^0.9.79-alpha.4",
|
"@budibase/standard-components": "^0.9.80-alpha.9",
|
||||||
"@jest/test-sequencer": "^24.8.0",
|
"@jest/test-sequencer": "^24.8.0",
|
||||||
"@types/bull": "^3.15.1",
|
"@types/bull": "^3.15.1",
|
||||||
"@types/jest": "^26.0.23",
|
"@types/jest": "^26.0.23",
|
||||||
|
@ -134,6 +134,7 @@
|
||||||
"nodemon": "^2.0.4",
|
"nodemon": "^2.0.4",
|
||||||
"pouchdb-adapter-memory": "^7.2.1",
|
"pouchdb-adapter-memory": "^7.2.1",
|
||||||
"prettier": "^2.3.1",
|
"prettier": "^2.3.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
"supertest": "^4.0.2",
|
"supertest": "^4.0.2",
|
||||||
"ts-jest": "^27.0.3",
|
"ts-jest": "^27.0.3",
|
||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
|
|
|
@ -29,11 +29,11 @@
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"svelte"
|
"svelte"
|
||||||
],
|
],
|
||||||
"version": "0.9.79-alpha.4",
|
"version": "0.9.80-alpha.9",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc",
|
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^0.9.79-alpha.4",
|
"@budibase/bbui": "^0.9.80-alpha.9",
|
||||||
"@spectrum-css/card": "^3.0.3",
|
"@spectrum-css/card": "^3.0.3",
|
||||||
"@spectrum-css/link": "^3.1.3",
|
"@spectrum-css/link": "^3.1.3",
|
||||||
"@spectrum-css/page": "^3.0.1",
|
"@spectrum-css/page": "^3.0.1",
|
||||||
|
|
|
@ -23,7 +23,7 @@ export const buildLuceneQuery = filter => {
|
||||||
value = parseFloat(value)
|
value = parseFloat(value)
|
||||||
}
|
}
|
||||||
if (type === "boolean") {
|
if (type === "boolean") {
|
||||||
value = value?.toLowerCase() === "true"
|
value = `${value}`?.toLowerCase() === "true"
|
||||||
}
|
}
|
||||||
if (operator.startsWith("range")) {
|
if (operator.startsWith("range")) {
|
||||||
if (!query.range[field]) {
|
if (!query.range[field]) {
|
||||||
|
@ -91,6 +91,11 @@ export const luceneQuery = (docs, query) => {
|
||||||
return !doc[key] || !doc[key].startsWith(value)
|
return !doc[key] || !doc[key].startsWith(value)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Process a fuzzy match (treat the same as starts with when running locally)
|
||||||
|
const fuzzyMatch = match("fuzzy", (key, value, doc) => {
|
||||||
|
return !doc[key] || !doc[key].startsWith(value)
|
||||||
|
})
|
||||||
|
|
||||||
// Process a range match
|
// Process a range match
|
||||||
const rangeMatch = match("range", (key, value, doc) => {
|
const rangeMatch = match("range", (key, value, doc) => {
|
||||||
return !doc[key] || doc[key] < value.low || doc[key] > value.high
|
return !doc[key] || doc[key] < value.low || doc[key] > value.high
|
||||||
|
@ -120,6 +125,7 @@ export const luceneQuery = (docs, query) => {
|
||||||
const docMatch = doc => {
|
const docMatch = doc => {
|
||||||
return (
|
return (
|
||||||
stringMatch(doc) &&
|
stringMatch(doc) &&
|
||||||
|
fuzzyMatch(doc) &&
|
||||||
rangeMatch(doc) &&
|
rangeMatch(doc) &&
|
||||||
equalMatch(doc) &&
|
equalMatch(doc) &&
|
||||||
notEqualMatch(doc) &&
|
notEqualMatch(doc) &&
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/string-templates",
|
"name": "@budibase/string-templates",
|
||||||
"version": "0.9.79-alpha.4",
|
"version": "0.9.80-alpha.9",
|
||||||
"description": "Handlebars wrapper for Budibase templating.",
|
"description": "Handlebars wrapper for Budibase templating.",
|
||||||
"main": "src/index.cjs",
|
"main": "src/index.cjs",
|
||||||
"module": "dist/bundle.mjs",
|
"module": "dist/bundle.mjs",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/worker",
|
"name": "@budibase/worker",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "0.9.79-alpha.4",
|
"version": "0.9.80-alpha.9",
|
||||||
"description": "Budibase background service",
|
"description": "Budibase background service",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -23,8 +23,8 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/auth": "^0.9.79-alpha.4",
|
"@budibase/auth": "^0.9.80-alpha.9",
|
||||||
"@budibase/string-templates": "^0.9.79-alpha.4",
|
"@budibase/string-templates": "^0.9.80-alpha.9",
|
||||||
"@koa/router": "^8.0.0",
|
"@koa/router": "^8.0.0",
|
||||||
"@techpass/passport-openidconnect": "^0.3.0",
|
"@techpass/passport-openidconnect": "^0.3.0",
|
||||||
"aws-sdk": "^2.811.0",
|
"aws-sdk": "^2.811.0",
|
||||||
|
|
Loading…
Reference in New Issue