Merge pull request #11988 from Budibase/budi-7589/user-column-multi-user-filtering-support

Multi user column
This commit is contained in:
Adria Navarro 2023-10-09 11:52:22 +02:00 committed by GitHub
commit 6705b67b67
12 changed files with 167 additions and 78 deletions

View File

@ -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

View File

@ -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}

View File

@ -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 = {

View File

@ -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

View File

@ -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
)} )}

View File

@ -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`
} }

View File

@ -21,6 +21,7 @@ const TypeIconMap = {
bigint: "TagBold", bigint: "TagBold",
bb_reference: { bb_reference: {
user: "User", user: "User",
users: "UserGroup",
}, },
} }

View File

@ -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(),

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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,
}, },
} }