Merge pull request #11988 from Budibase/budi-7589/user-column-multi-user-filtering-support
Multi user column
This commit is contained in:
commit
6705b67b67
|
@ -33,7 +33,7 @@
|
||||||
import { getBindings } from "components/backend/DataTable/formula"
|
import { getBindings } from "components/backend/DataTable/formula"
|
||||||
import JSONSchemaModal from "./JSONSchemaModal.svelte"
|
import JSONSchemaModal from "./JSONSchemaModal.svelte"
|
||||||
import { ValidColumnNameRegex } from "@budibase/shared-core"
|
import { ValidColumnNameRegex } from "@budibase/shared-core"
|
||||||
import { FieldType } from "@budibase/types"
|
import { FieldType, FieldSubtype, SourceName } from "@budibase/types"
|
||||||
import RelationshipSelector from "components/common/RelationshipSelector.svelte"
|
import RelationshipSelector from "components/common/RelationshipSelector.svelte"
|
||||||
|
|
||||||
const AUTO_TYPE = "auto"
|
const AUTO_TYPE = "auto"
|
||||||
|
@ -43,7 +43,6 @@
|
||||||
const NUMBER_TYPE = FIELDS.NUMBER.type
|
const NUMBER_TYPE = FIELDS.NUMBER.type
|
||||||
const JSON_TYPE = FIELDS.JSON.type
|
const JSON_TYPE = FIELDS.JSON.type
|
||||||
const DATE_TYPE = FIELDS.DATETIME.type
|
const DATE_TYPE = FIELDS.DATETIME.type
|
||||||
const USER_REFRENCE_TYPE = FIELDS.BB_REFERENCE_USER.compositeType
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const PROHIBITED_COLUMN_NAMES = ["type", "_id", "_rev", "tableId"]
|
const PROHIBITED_COLUMN_NAMES = ["type", "_id", "_rev", "tableId"]
|
||||||
|
@ -52,7 +51,19 @@
|
||||||
export let field
|
export let field
|
||||||
|
|
||||||
let mounted = false
|
let mounted = false
|
||||||
let fieldDefinitions = cloneDeep(FIELDS)
|
const fieldDefinitions = Object.values(FIELDS).reduce(
|
||||||
|
// Storing the fields by complex field id
|
||||||
|
(acc, field) => ({
|
||||||
|
...acc,
|
||||||
|
[makeFieldId(field.type, field.subtype)]: field,
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
function makeFieldId(type, subtype) {
|
||||||
|
return `${type}${subtype || ""}`.toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
let originalName
|
let originalName
|
||||||
let linkEditDisabled
|
let linkEditDisabled
|
||||||
let primaryDisplay
|
let primaryDisplay
|
||||||
|
@ -72,8 +83,8 @@
|
||||||
let jsonSchemaModal
|
let jsonSchemaModal
|
||||||
let allowedTypes = []
|
let allowedTypes = []
|
||||||
let editableColumn = {
|
let editableColumn = {
|
||||||
type: fieldDefinitions.STRING.type,
|
type: FIELDS.STRING.type,
|
||||||
constraints: fieldDefinitions.STRING.constraints,
|
constraints: FIELDS.STRING.constraints,
|
||||||
// Initial value for column name in other table for linked records
|
// Initial value for column name in other table for linked records
|
||||||
fieldName: $tables.selected.name,
|
fieldName: $tables.selected.name,
|
||||||
}
|
}
|
||||||
|
@ -139,9 +150,6 @@
|
||||||
$tables.selected.primaryDisplay == null ||
|
$tables.selected.primaryDisplay == null ||
|
||||||
$tables.selected.primaryDisplay === editableColumn.name
|
$tables.selected.primaryDisplay === editableColumn.name
|
||||||
|
|
||||||
if (editableColumn.type === FieldType.BB_REFERENCE) {
|
|
||||||
editableColumn.type = `${editableColumn.type}_${editableColumn.subtype}`
|
|
||||||
}
|
|
||||||
// Here we are setting the relationship values based on the editableColumn
|
// Here we are setting the relationship values based on the editableColumn
|
||||||
// This part of the code is used when viewing an existing field hence the check
|
// This part of the code is used when viewing an existing field hence the check
|
||||||
// for the tableId
|
// for the tableId
|
||||||
|
@ -172,7 +180,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allowedTypes = getAllowedTypes()
|
if (!savingColumn) {
|
||||||
|
editableColumn.fieldId = makeFieldId(
|
||||||
|
editableColumn.type,
|
||||||
|
editableColumn.subtype
|
||||||
|
)
|
||||||
|
|
||||||
|
allowedTypes = getAllowedTypes().map(t => ({
|
||||||
|
fieldId: makeFieldId(t.type, t.subtype),
|
||||||
|
...t,
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: initialiseField(field, savingColumn)
|
$: initialiseField(field, savingColumn)
|
||||||
|
@ -249,13 +267,7 @@
|
||||||
|
|
||||||
let saveColumn = cloneDeep(editableColumn)
|
let saveColumn = cloneDeep(editableColumn)
|
||||||
|
|
||||||
// Handle types on composite types
|
delete saveColumn.fieldId
|
||||||
const definition = fieldDefinitions[saveColumn.type.toUpperCase()]
|
|
||||||
if (definition && saveColumn.type === definition.compositeType) {
|
|
||||||
saveColumn.type = definition.type
|
|
||||||
saveColumn.subtype = definition.subtype
|
|
||||||
delete saveColumn.compositeType
|
|
||||||
}
|
|
||||||
|
|
||||||
if (saveColumn.type === AUTO_TYPE) {
|
if (saveColumn.type === AUTO_TYPE) {
|
||||||
saveColumn = buildAutoColumn(
|
saveColumn = buildAutoColumn(
|
||||||
|
@ -320,27 +332,33 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTypeChange(event) {
|
function onHandleTypeChange(event) {
|
||||||
|
handleTypeChange(event.detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTypeChange(type) {
|
||||||
// remove any extra fields that may not be related to this type
|
// remove any extra fields that may not be related to this type
|
||||||
delete editableColumn.autocolumn
|
delete editableColumn.autocolumn
|
||||||
delete editableColumn.subtype
|
delete editableColumn.subtype
|
||||||
delete editableColumn.tableId
|
delete editableColumn.tableId
|
||||||
delete editableColumn.relationshipType
|
delete editableColumn.relationshipType
|
||||||
delete editableColumn.formulaType
|
delete editableColumn.formulaType
|
||||||
|
delete editableColumn.constraints
|
||||||
|
|
||||||
// Add in defaults and initial definition
|
// Add in defaults and initial definition
|
||||||
const definition = fieldDefinitions[event.detail?.toUpperCase()]
|
const definition = fieldDefinitions[type?.toUpperCase()]
|
||||||
if (definition?.constraints) {
|
if (definition?.constraints) {
|
||||||
editableColumn.constraints = definition.constraints
|
editableColumn.constraints = definition.constraints
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editableColumn.type = definition.type
|
||||||
|
editableColumn.subtype = definition.subtype
|
||||||
|
|
||||||
// Default relationships many to many
|
// Default relationships many to many
|
||||||
if (editableColumn.type === LINK_TYPE) {
|
if (editableColumn.type === LINK_TYPE) {
|
||||||
editableColumn.relationshipType = RelationshipType.MANY_TO_MANY
|
editableColumn.relationshipType = RelationshipType.MANY_TO_MANY
|
||||||
} else if (editableColumn.type === FORMULA_TYPE) {
|
} else if (editableColumn.type === FORMULA_TYPE) {
|
||||||
editableColumn.formulaType = "dynamic"
|
editableColumn.formulaType = "dynamic"
|
||||||
} else if (editableColumn.type === USER_REFRENCE_TYPE) {
|
|
||||||
editableColumn.relationshipType = RelationshipType.ONE_TO_MANY
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,9 +399,26 @@
|
||||||
return ALLOWABLE_NUMBER_OPTIONS
|
return ALLOWABLE_NUMBER_OPTIONS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isUsers =
|
||||||
|
editableColumn.type === FieldType.BB_REFERENCE &&
|
||||||
|
editableColumn.subtype === FieldSubtype.USERS
|
||||||
|
|
||||||
if (!external) {
|
if (!external) {
|
||||||
return [
|
return [
|
||||||
...Object.values(fieldDefinitions),
|
FIELDS.STRING,
|
||||||
|
FIELDS.BARCODEQR,
|
||||||
|
FIELDS.LONGFORM,
|
||||||
|
FIELDS.OPTIONS,
|
||||||
|
FIELDS.ARRAY,
|
||||||
|
FIELDS.NUMBER,
|
||||||
|
FIELDS.BIGINT,
|
||||||
|
FIELDS.BOOLEAN,
|
||||||
|
FIELDS.DATETIME,
|
||||||
|
FIELDS.ATTACHMENT,
|
||||||
|
FIELDS.LINK,
|
||||||
|
FIELDS.FORMULA,
|
||||||
|
FIELDS.JSON,
|
||||||
|
isUsers ? FIELDS.USERS : FIELDS.USER,
|
||||||
{ name: "Auto Column", type: AUTO_TYPE },
|
{ name: "Auto Column", type: AUTO_TYPE },
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
|
@ -397,7 +432,7 @@
|
||||||
FIELDS.BOOLEAN,
|
FIELDS.BOOLEAN,
|
||||||
FIELDS.FORMULA,
|
FIELDS.FORMULA,
|
||||||
FIELDS.BIGINT,
|
FIELDS.BIGINT,
|
||||||
FIELDS.BB_REFERENCE_USER,
|
isUsers ? FIELDS.USERS : FIELDS.USER,
|
||||||
]
|
]
|
||||||
// no-sql or a spreadsheet
|
// no-sql or a spreadsheet
|
||||||
if (!external || table.sql) {
|
if (!external || table.sql) {
|
||||||
|
@ -472,6 +507,13 @@
|
||||||
return newError
|
return newError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isUsersColumn(column) {
|
||||||
|
return (
|
||||||
|
column.type === FieldType.BB_REFERENCE &&
|
||||||
|
[FieldSubtype.USER, FieldSubtype.USERS].includes(column.subtype)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
mounted = true
|
mounted = true
|
||||||
})
|
})
|
||||||
|
@ -489,11 +531,11 @@
|
||||||
{/if}
|
{/if}
|
||||||
<Select
|
<Select
|
||||||
disabled={!typeEnabled}
|
disabled={!typeEnabled}
|
||||||
bind:value={editableColumn.type}
|
bind:value={editableColumn.fieldId}
|
||||||
on:change={handleTypeChange}
|
on:change={onHandleTypeChange}
|
||||||
options={allowedTypes}
|
options={allowedTypes}
|
||||||
getOptionLabel={field => field.name}
|
getOptionLabel={field => field.name}
|
||||||
getOptionValue={field => field.compositeType || field.type}
|
getOptionValue={field => field.fieldId}
|
||||||
getOptionIcon={field => field.icon}
|
getOptionIcon={field => field.icon}
|
||||||
isOptionEnabled={option => {
|
isOptionEnabled={option => {
|
||||||
if (option.type == AUTO_TYPE) {
|
if (option.type == AUTO_TYPE) {
|
||||||
|
@ -555,7 +597,7 @@
|
||||||
<DatePicker bind:value={editableColumn.constraints.datetime.latest} />
|
<DatePicker bind:value={editableColumn.constraints.datetime.latest} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if datasource?.source !== "ORACLE" && datasource?.source !== "SQL_SERVER" && !editableColumn.dateOnly}
|
{#if datasource?.source !== SourceName.ORACLE && datasource?.source !== SourceName.SQL_SERVER && !editableColumn.dateOnly}
|
||||||
<div>
|
<div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<Label>Time zones</Label>
|
<Label>Time zones</Label>
|
||||||
|
@ -659,18 +701,20 @@
|
||||||
<Button primary text on:click={openJsonSchemaEditor}
|
<Button primary text on:click={openJsonSchemaEditor}
|
||||||
>Open schema editor</Button
|
>Open schema editor</Button
|
||||||
>
|
>
|
||||||
{:else if editableColumn.type === USER_REFRENCE_TYPE}
|
{:else if isUsersColumn(editableColumn) && datasource?.source !== SourceName.GOOGLE_SHEETS}
|
||||||
<!-- Disabled temporally -->
|
<Toggle
|
||||||
<!-- <Toggle
|
value={editableColumn.subtype === FieldSubtype.USERS}
|
||||||
value={editableColumn.relationshipType === RelationshipType.MANY_TO_MANY}
|
|
||||||
on:change={e =>
|
on:change={e =>
|
||||||
(editableColumn.relationshipType = e.detail
|
handleTypeChange(
|
||||||
? RelationshipType.MANY_TO_MANY
|
makeFieldId(
|
||||||
: RelationshipType.ONE_TO_MANY)}
|
FieldType.BB_REFERENCE,
|
||||||
|
e.detail ? FieldSubtype.USERS : FieldSubtype.USER
|
||||||
|
)
|
||||||
|
)}
|
||||||
disabled={!isCreating}
|
disabled={!isCreating}
|
||||||
thin
|
thin
|
||||||
text="Allow multiple users"
|
text="Allow multiple users"
|
||||||
/> -->
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{#if editableColumn.type === AUTO_TYPE || editableColumn.autocolumn}
|
{#if editableColumn.type === AUTO_TYPE || editableColumn.autocolumn}
|
||||||
<Select
|
<Select
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
import { FieldType } from "@budibase/types"
|
import { FieldType } from "@budibase/types"
|
||||||
import { createEventDispatcher, onMount } from "svelte"
|
import { createEventDispatcher, onMount } from "svelte"
|
||||||
import FilterUsers from "./FilterUsers.svelte"
|
import FilterUsers from "./FilterUsers.svelte"
|
||||||
import { RelationshipType } from "constants/backend"
|
|
||||||
|
|
||||||
export let schemaFields
|
export let schemaFields
|
||||||
export let filters = []
|
export let filters = []
|
||||||
|
@ -126,6 +125,7 @@
|
||||||
// Update type based on field
|
// Update type based on field
|
||||||
const fieldSchema = enrichedSchemaFields.find(x => x.name === filter.field)
|
const fieldSchema = enrichedSchemaFields.find(x => x.name === filter.field)
|
||||||
filter.type = fieldSchema?.type
|
filter.type = fieldSchema?.type
|
||||||
|
filter.subtype = fieldSchema?.subtype
|
||||||
|
|
||||||
// Update external type based on field
|
// Update external type based on field
|
||||||
filter.externalType = getSchema(filter)?.externalType
|
filter.externalType = getSchema(filter)?.externalType
|
||||||
|
@ -196,7 +196,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
return LuceneUtils.getValidOperatorsForType(
|
return LuceneUtils.getValidOperatorsForType(
|
||||||
filter.type,
|
{ type: filter.type, subtype: filter.subtype },
|
||||||
filter.field,
|
filter.field,
|
||||||
datasource
|
datasource
|
||||||
)
|
)
|
||||||
|
@ -301,9 +301,10 @@
|
||||||
{:else if filter.type === FieldType.BB_REFERENCE}
|
{:else if filter.type === FieldType.BB_REFERENCE}
|
||||||
<FilterUsers
|
<FilterUsers
|
||||||
bind:value={filter.value}
|
bind:value={filter.value}
|
||||||
multiselect={getSchema(filter).relationshipType ===
|
multiselect={[
|
||||||
RelationshipType.MANY_TO_MANY ||
|
OperatorOptions.In.value,
|
||||||
filter.operator === OperatorOptions.In.value}
|
OperatorOptions.ContainsAny.value,
|
||||||
|
].includes(filter.operator)}
|
||||||
disabled={filter.noValue}
|
disabled={filter.noValue}
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
import { FieldType, FieldSubtype } from "@budibase/types"
|
||||||
|
|
||||||
export const FIELDS = {
|
export const FIELDS = {
|
||||||
STRING: {
|
STRING: {
|
||||||
name: "Text",
|
name: "Text",
|
||||||
type: "string",
|
type: FieldType.STRING,
|
||||||
icon: "Text",
|
icon: "Text",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "string",
|
type: "string",
|
||||||
|
@ -11,7 +13,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
BARCODEQR: {
|
BARCODEQR: {
|
||||||
name: "Barcode/QR",
|
name: "Barcode/QR",
|
||||||
type: "barcodeqr",
|
type: FieldType.BARCODEQR,
|
||||||
icon: "Camera",
|
icon: "Camera",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "string",
|
type: "string",
|
||||||
|
@ -21,7 +23,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
LONGFORM: {
|
LONGFORM: {
|
||||||
name: "Long Form Text",
|
name: "Long Form Text",
|
||||||
type: "longform",
|
type: FieldType.LONGFORM,
|
||||||
icon: "TextAlignLeft",
|
icon: "TextAlignLeft",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "string",
|
type: "string",
|
||||||
|
@ -31,7 +33,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
OPTIONS: {
|
OPTIONS: {
|
||||||
name: "Options",
|
name: "Options",
|
||||||
type: "options",
|
type: FieldType.OPTIONS,
|
||||||
icon: "Dropdown",
|
icon: "Dropdown",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "string",
|
type: "string",
|
||||||
|
@ -41,7 +43,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
ARRAY: {
|
ARRAY: {
|
||||||
name: "Multi-select",
|
name: "Multi-select",
|
||||||
type: "array",
|
type: FieldType.ARRAY,
|
||||||
icon: "Duplicate",
|
icon: "Duplicate",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "array",
|
type: "array",
|
||||||
|
@ -51,7 +53,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
NUMBER: {
|
NUMBER: {
|
||||||
name: "Number",
|
name: "Number",
|
||||||
type: "number",
|
type: FieldType.NUMBER,
|
||||||
icon: "123",
|
icon: "123",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "number",
|
type: "number",
|
||||||
|
@ -61,12 +63,12 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
BIGINT: {
|
BIGINT: {
|
||||||
name: "BigInt",
|
name: "BigInt",
|
||||||
type: "bigint",
|
type: FieldType.BIGINT,
|
||||||
icon: "TagBold",
|
icon: "TagBold",
|
||||||
},
|
},
|
||||||
BOOLEAN: {
|
BOOLEAN: {
|
||||||
name: "Boolean",
|
name: "Boolean",
|
||||||
type: "boolean",
|
type: FieldType.BOOLEAN,
|
||||||
icon: "Boolean",
|
icon: "Boolean",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
|
@ -75,7 +77,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
DATETIME: {
|
DATETIME: {
|
||||||
name: "Date/Time",
|
name: "Date/Time",
|
||||||
type: "datetime",
|
type: FieldType.DATETIME,
|
||||||
icon: "Calendar",
|
icon: "Calendar",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "string",
|
type: "string",
|
||||||
|
@ -89,7 +91,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
ATTACHMENT: {
|
ATTACHMENT: {
|
||||||
name: "Attachment",
|
name: "Attachment",
|
||||||
type: "attachment",
|
type: FieldType.ATTACHMENT,
|
||||||
icon: "Folder",
|
icon: "Folder",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "array",
|
type: "array",
|
||||||
|
@ -98,7 +100,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
LINK: {
|
LINK: {
|
||||||
name: "Relationship",
|
name: "Relationship",
|
||||||
type: "link",
|
type: FieldType.LINK,
|
||||||
icon: "Link",
|
icon: "Link",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "array",
|
type: "array",
|
||||||
|
@ -107,26 +109,34 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
FORMULA: {
|
FORMULA: {
|
||||||
name: "Formula",
|
name: "Formula",
|
||||||
type: "formula",
|
type: FieldType.FORMULA,
|
||||||
icon: "Calculator",
|
icon: "Calculator",
|
||||||
constraints: {},
|
constraints: {},
|
||||||
},
|
},
|
||||||
JSON: {
|
JSON: {
|
||||||
name: "JSON",
|
name: "JSON",
|
||||||
type: "json",
|
type: FieldType.JSON,
|
||||||
icon: "Brackets",
|
icon: "Brackets",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "object",
|
type: "object",
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
BB_REFERENCE_USER: {
|
USER: {
|
||||||
name: "User",
|
name: "User",
|
||||||
type: "bb_reference",
|
type: FieldType.BB_REFERENCE,
|
||||||
subtype: "user",
|
subtype: FieldSubtype.USER,
|
||||||
compositeType: "bb_reference_user", // Used for working with the subtype on CreateEditColumn as is it was a primary type
|
|
||||||
icon: "User",
|
icon: "User",
|
||||||
},
|
},
|
||||||
|
USERS: {
|
||||||
|
name: "Users",
|
||||||
|
type: FieldType.BB_REFERENCE,
|
||||||
|
subtype: FieldSubtype.USERS,
|
||||||
|
icon: "User",
|
||||||
|
constraints: {
|
||||||
|
type: "array",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AUTO_COLUMN_SUB_TYPES = {
|
export const AUTO_COLUMN_SUB_TYPES = {
|
||||||
|
|
|
@ -118,7 +118,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const getOperatorOptions = condition => {
|
const getOperatorOptions = condition => {
|
||||||
return LuceneUtils.getValidOperatorsForType(condition.valueType)
|
return LuceneUtils.getValidOperatorsForType({ type: condition.valueType })
|
||||||
}
|
}
|
||||||
|
|
||||||
const onOperatorChange = (condition, newOperator) => {
|
const onOperatorChange = (condition, newOperator) => {
|
||||||
|
@ -137,9 +137,9 @@
|
||||||
condition.referenceValue = null
|
condition.referenceValue = null
|
||||||
|
|
||||||
// Ensure a valid operator is set
|
// Ensure a valid operator is set
|
||||||
const validOperators = LuceneUtils.getValidOperatorsForType(newType).map(
|
const validOperators = LuceneUtils.getValidOperatorsForType({
|
||||||
x => x.value
|
type: newType,
|
||||||
)
|
}).map(x => x.value)
|
||||||
if (!validOperators.includes(condition.operator)) {
|
if (!validOperators.includes(condition.operator)) {
|
||||||
condition.operator =
|
condition.operator =
|
||||||
validOperators[0] ?? Constants.OperatorOptions.Equals.value
|
validOperators[0] ?? Constants.OperatorOptions.Equals.value
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
|
|
||||||
// Ensure a valid operator is set
|
// Ensure a valid operator is set
|
||||||
const validOperators = LuceneUtils.getValidOperatorsForType(
|
const validOperators = LuceneUtils.getValidOperatorsForType(
|
||||||
expression.type,
|
{ type: expression.type },
|
||||||
expression.field,
|
expression.field,
|
||||||
datasource
|
datasource
|
||||||
).map(x => x.value)
|
).map(x => x.value)
|
||||||
|
@ -125,7 +125,7 @@
|
||||||
<Select
|
<Select
|
||||||
disabled={!filter.field}
|
disabled={!filter.field}
|
||||||
options={LuceneUtils.getValidOperatorsForType(
|
options={LuceneUtils.getValidOperatorsForType(
|
||||||
filter.type,
|
{ type: filter.type, subtype: filter.subtype },
|
||||||
filter.field,
|
filter.field,
|
||||||
datasource
|
datasource
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import RelationshipCell from "./RelationshipCell.svelte"
|
import RelationshipCell from "./RelationshipCell.svelte"
|
||||||
import { FieldSubtype } from "@budibase/types"
|
import { FieldSubtype, RelationshipType } from "@budibase/types"
|
||||||
|
|
||||||
export let api
|
export let api
|
||||||
|
|
||||||
|
@ -12,10 +12,14 @@
|
||||||
...$$props.schema,
|
...$$props.schema,
|
||||||
// This is not really used, just adding some content to be able to render the relationship cell
|
// This is not really used, just adding some content to be able to render the relationship cell
|
||||||
tableId: "external",
|
tableId: "external",
|
||||||
|
relationshipType:
|
||||||
|
subtype === FieldSubtype.USER
|
||||||
|
? RelationshipType.ONE_TO_MANY
|
||||||
|
: RelationshipType.MANY_TO_MANY,
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchFunction(searchParams) {
|
async function searchFunction(searchParams) {
|
||||||
if (subtype !== FieldSubtype.USER) {
|
if (subtype !== FieldSubtype.USER && subtype !== FieldSubtype.USERS) {
|
||||||
throw `Search for '${subtype}' not implemented`
|
throw `Search for '${subtype}' not implemented`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ const TypeIconMap = {
|
||||||
bigint: "TagBold",
|
bigint: "TagBold",
|
||||||
bb_reference: {
|
bb_reference: {
|
||||||
user: "User",
|
user: "User",
|
||||||
|
users: "UserGroup",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1566,15 +1566,13 @@ describe.each([
|
||||||
() => ({
|
() => ({
|
||||||
user: {
|
user: {
|
||||||
name: "user",
|
name: "user",
|
||||||
relationshipType: RelationshipType.ONE_TO_MANY,
|
|
||||||
type: FieldType.BB_REFERENCE,
|
type: FieldType.BB_REFERENCE,
|
||||||
subtype: FieldTypeSubtypes.BB_REFERENCE.USER,
|
subtype: FieldTypeSubtypes.BB_REFERENCE.USER,
|
||||||
},
|
},
|
||||||
users: {
|
users: {
|
||||||
name: "users",
|
name: "users",
|
||||||
type: FieldType.BB_REFERENCE,
|
type: FieldType.BB_REFERENCE,
|
||||||
subtype: FieldTypeSubtypes.BB_REFERENCE.USER,
|
subtype: FieldTypeSubtypes.BB_REFERENCE.USERS,
|
||||||
relationshipType: RelationshipType.MANY_TO_MANY,
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
() => config.createUser(),
|
() => config.createUser(),
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
import { Knex, knex } from "knex"
|
import { Knex, knex } from "knex"
|
||||||
import { Operation, QueryJson, RenameColumn, Table } from "@budibase/types"
|
import {
|
||||||
|
FieldSubtype,
|
||||||
|
Operation,
|
||||||
|
QueryJson,
|
||||||
|
RenameColumn,
|
||||||
|
Table,
|
||||||
|
} from "@budibase/types"
|
||||||
import { breakExternalTableId } from "../utils"
|
import { breakExternalTableId } from "../utils"
|
||||||
import SchemaBuilder = Knex.SchemaBuilder
|
import SchemaBuilder = Knex.SchemaBuilder
|
||||||
import CreateTableBuilder = Knex.CreateTableBuilder
|
import CreateTableBuilder = Knex.CreateTableBuilder
|
||||||
import { FieldTypes, RelationshipType } from "../../constants"
|
import { FieldTypes, RelationshipType } from "../../constants"
|
||||||
|
import { utils } from "@budibase/shared-core"
|
||||||
|
|
||||||
function generateSchema(
|
function generateSchema(
|
||||||
schema: CreateTableBuilder,
|
schema: CreateTableBuilder,
|
||||||
|
@ -41,9 +48,21 @@ function generateSchema(
|
||||||
case FieldTypes.OPTIONS:
|
case FieldTypes.OPTIONS:
|
||||||
case FieldTypes.LONGFORM:
|
case FieldTypes.LONGFORM:
|
||||||
case FieldTypes.BARCODEQR:
|
case FieldTypes.BARCODEQR:
|
||||||
case FieldTypes.BB_REFERENCE:
|
|
||||||
schema.text(key)
|
schema.text(key)
|
||||||
break
|
break
|
||||||
|
case FieldTypes.BB_REFERENCE:
|
||||||
|
const subtype = column.subtype as FieldSubtype
|
||||||
|
switch (subtype) {
|
||||||
|
case FieldSubtype.USER:
|
||||||
|
schema.text(key)
|
||||||
|
break
|
||||||
|
case FieldSubtype.USERS:
|
||||||
|
schema.json(key)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw utils.unreachable(subtype)
|
||||||
|
}
|
||||||
|
break
|
||||||
case FieldTypes.NUMBER:
|
case FieldTypes.NUMBER:
|
||||||
// if meta is specified then this is a junction table entry
|
// if meta is specified then this is a junction table entry
|
||||||
if (column.meta && column.meta.toKey && column.meta.toTable) {
|
if (column.meta && column.meta.toKey && column.meta.toTable) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ const ROW_PREFIX = DocumentType.ROW + SEPARATOR
|
||||||
export async function processInputBBReferences(
|
export async function processInputBBReferences(
|
||||||
value: string | string[] | { _id: string } | { _id: string }[],
|
value: string | string[] | { _id: string } | { _id: string }[],
|
||||||
subtype: FieldSubtype
|
subtype: FieldSubtype
|
||||||
): Promise<string | null> {
|
): Promise<string | string[] | null> {
|
||||||
let referenceIds: string[] = []
|
let referenceIds: string[] = []
|
||||||
|
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
|
@ -41,33 +41,39 @@ export async function processInputBBReferences(
|
||||||
|
|
||||||
switch (subtype) {
|
switch (subtype) {
|
||||||
case FieldSubtype.USER:
|
case FieldSubtype.USER:
|
||||||
|
case FieldSubtype.USERS:
|
||||||
const { notFoundIds } = await cache.user.getUsers(referenceIds)
|
const { notFoundIds } = await cache.user.getUsers(referenceIds)
|
||||||
|
|
||||||
if (notFoundIds?.length) {
|
if (notFoundIds?.length) {
|
||||||
throw new InvalidBBRefError(notFoundIds[0], FieldSubtype.USER)
|
throw new InvalidBBRefError(notFoundIds[0], FieldSubtype.USER)
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
if (subtype === FieldSubtype.USERS) {
|
||||||
|
return referenceIds
|
||||||
|
}
|
||||||
|
|
||||||
|
return referenceIds.join(",") || null
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw utils.unreachable(subtype)
|
throw utils.unreachable(subtype)
|
||||||
}
|
}
|
||||||
|
|
||||||
return referenceIds.join(",") || null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function processOutputBBReferences(
|
export async function processOutputBBReferences(
|
||||||
value: string,
|
value: string | string[],
|
||||||
subtype: FieldSubtype
|
subtype: FieldSubtype
|
||||||
) {
|
) {
|
||||||
if (typeof value !== "string") {
|
if (value === null || value === undefined) {
|
||||||
// Already processed or nothing to process
|
// Already processed or nothing to process
|
||||||
return value || undefined
|
return value || undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const ids = value.split(",").filter(id => !!id)
|
const ids =
|
||||||
|
typeof value === "string" ? value.split(",").filter(id => !!id) : value
|
||||||
|
|
||||||
switch (subtype) {
|
switch (subtype) {
|
||||||
case FieldSubtype.USER:
|
case FieldSubtype.USER:
|
||||||
|
case FieldSubtype.USERS:
|
||||||
const { users } = await cache.user.getUsers(ids)
|
const { users } = await cache.user.getUsers(ids)
|
||||||
if (!users.length) {
|
if (!users.length) {
|
||||||
return undefined
|
return undefined
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
SearchFilter,
|
SearchFilter,
|
||||||
SearchQuery,
|
SearchQuery,
|
||||||
SearchQueryFields,
|
SearchQueryFields,
|
||||||
|
FieldSubtype,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { OperatorOptions, SqlNumberTypeRangeMap } from "./constants"
|
import { OperatorOptions, SqlNumberTypeRangeMap } from "./constants"
|
||||||
import { deepGet } from "./helpers"
|
import { deepGet } from "./helpers"
|
||||||
|
@ -16,7 +17,7 @@ const HBS_REGEX = /{{([^{].*?)}}/g
|
||||||
* Returns the valid operator options for a certain data type
|
* Returns the valid operator options for a certain data type
|
||||||
*/
|
*/
|
||||||
export const getValidOperatorsForType = (
|
export const getValidOperatorsForType = (
|
||||||
type: FieldType,
|
fieldType: { type: FieldType; subtype?: FieldSubtype },
|
||||||
field: string,
|
field: string,
|
||||||
datasource: Datasource & { tableId: any } // TODO: is this table id ever populated?
|
datasource: Datasource & { tableId: any } // TODO: is this table id ever populated?
|
||||||
) => {
|
) => {
|
||||||
|
@ -43,6 +44,7 @@ export const getValidOperatorsForType = (
|
||||||
value: string
|
value: string
|
||||||
label: string
|
label: string
|
||||||
}[] = []
|
}[] = []
|
||||||
|
const { type, subtype } = fieldType
|
||||||
if (type === FieldType.STRING) {
|
if (type === FieldType.STRING) {
|
||||||
ops = stringOps
|
ops = stringOps
|
||||||
} else if (type === FieldType.NUMBER || type === FieldType.BIGINT) {
|
} else if (type === FieldType.NUMBER || type === FieldType.BIGINT) {
|
||||||
|
@ -59,8 +61,10 @@ export const getValidOperatorsForType = (
|
||||||
ops = numOps
|
ops = numOps
|
||||||
} else if (type === FieldType.FORMULA) {
|
} else if (type === FieldType.FORMULA) {
|
||||||
ops = stringOps.concat([Op.MoreThan, Op.LessThan])
|
ops = stringOps.concat([Op.MoreThan, Op.LessThan])
|
||||||
} else if (type === FieldType.BB_REFERENCE) {
|
} else if (type === FieldType.BB_REFERENCE && subtype == FieldSubtype.USER) {
|
||||||
ops = [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty, Op.In]
|
ops = [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty, Op.In]
|
||||||
|
} else if (type === FieldType.BB_REFERENCE && subtype == FieldSubtype.USERS) {
|
||||||
|
ops = [Op.Contains, Op.NotContains, Op.ContainsAny, Op.Empty, Op.NotEmpty]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only allow equal/not equal for _id in SQL tables
|
// Only allow equal/not equal for _id in SQL tables
|
||||||
|
|
|
@ -37,10 +37,12 @@ export interface Row extends Document {
|
||||||
|
|
||||||
export enum FieldSubtype {
|
export enum FieldSubtype {
|
||||||
USER = "user",
|
USER = "user",
|
||||||
|
USERS = "users",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FieldTypeSubtypes = {
|
export const FieldTypeSubtypes = {
|
||||||
BB_REFERENCE: {
|
BB_REFERENCE: {
|
||||||
USER: FieldSubtype.USER,
|
USER: FieldSubtype.USER,
|
||||||
|
USERS: FieldSubtype.USERS,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue