Make block searching on dates useful by using a range of the whole day

This commit is contained in:
Andrew Kingston 2022-07-22 11:10:20 +01:00
parent ed8ab95ae7
commit 303bfd5be4
6 changed files with 102 additions and 87 deletions

View File

@ -4,6 +4,7 @@
import BlockComponent from "components/BlockComponent.svelte" import BlockComponent from "components/BlockComponent.svelte"
import { Heading } from "@budibase/bbui" import { Heading } from "@budibase/bbui"
import { makePropSafe as safe } from "@budibase/string-templates" import { makePropSafe as safe } from "@budibase/string-templates"
import { enrichSearchColumns, enrichFilter } from "utils/blocks.js"
export let title export let title
export let dataSource export let dataSource
@ -33,14 +34,6 @@
const { fetchDatasourceSchema, styleable } = getContext("sdk") const { fetchDatasourceSchema, styleable } = getContext("sdk")
const context = getContext("context") const context = getContext("context")
const component = getContext("component") const component = getContext("component")
const schemaComponentMap = {
string: "stringfield",
options: "optionsfield",
number: "numberfield",
datetime: "datetimefield",
boolean: "booleanfield",
formula: "stringfield",
}
let formId let formId
let dataProviderId let dataProviderId
@ -68,39 +61,6 @@
}, },
] ]
// Enrich the default filter with the specified search fields
const enrichFilter = (filter, columns, formId) => {
let enrichedFilter = [...(filter || [])]
columns?.forEach(column => {
const safePath = column.name.split(".").map(safe).join(".")
enrichedFilter.push({
field: column.name,
operator: column.type === "string" ? "string" : "equal",
type: column.type,
valueType: "Binding",
value: `{{ ${safe(formId)}.${safePath} }}`,
})
})
return enrichedFilter
}
// Determine data types for search fields and only use those that are valid
const enrichSearchColumns = (searchColumns, schema) => {
let enrichedColumns = []
searchColumns?.forEach(column => {
const schemaType = schema?.[column]?.type
const componentType = schemaComponentMap[schemaType]
if (componentType) {
enrichedColumns.push({
name: column,
componentType,
type: schemaType,
})
}
})
return enrichedColumns.slice(0, 5)
}
// Builds a full details page URL for the card title // Builds a full details page URL for the card title
const buildFullCardUrl = (link, url, repeaterId, linkColumn) => { const buildFullCardUrl = (link, url, repeaterId, linkColumn) => {
if (!link || !url || !repeaterId) { if (!link || !url || !repeaterId) {

View File

@ -4,6 +4,7 @@
import BlockComponent from "components/BlockComponent.svelte" import BlockComponent from "components/BlockComponent.svelte"
import { Heading } from "@budibase/bbui" import { Heading } from "@budibase/bbui"
import { makePropSafe as safe } from "@budibase/string-templates" import { makePropSafe as safe } from "@budibase/string-templates"
import { enrichSearchColumns, enrichFilter } from "utils/blocks.js"
export let title export let title
export let dataSource export let dataSource
@ -31,14 +32,6 @@
const { fetchDatasourceSchema, styleable } = getContext("sdk") const { fetchDatasourceSchema, styleable } = getContext("sdk")
const context = getContext("context") const context = getContext("context")
const component = getContext("component") const component = getContext("component")
const schemaComponentMap = {
string: "stringfield",
options: "optionsfield",
number: "numberfield",
datetime: "datetimefield",
boolean: "booleanfield",
formula: "stringfield",
}
let formId let formId
let dataProviderId let dataProviderId
@ -58,40 +51,6 @@
}, },
] ]
// Enrich the default filter with the specified search fields
const enrichFilter = (filter, columns, formId) => {
let enrichedFilter = [...(filter || [])]
columns?.forEach(column => {
const safePath = column.name.split(".").map(safe).join(".")
const stringType = column.type === "string" || column.type === "formula"
enrichedFilter.push({
field: column.name,
type: column.type,
operator: stringType ? "string" : "equal",
valueType: "Binding",
value: `{{ ${safe(formId)}.${safePath} }}`,
})
})
return enrichedFilter
}
// Determine data types for search fields and only use those that are valid
const enrichSearchColumns = (searchColumns, schema) => {
let enrichedColumns = []
searchColumns?.forEach(column => {
const schemaType = schema?.[column]?.type
const componentType = schemaComponentMap[schemaType]
if (componentType) {
enrichedColumns.push({
name: column,
componentType,
type: schemaType,
})
}
})
return enrichedColumns.slice(0, 5)
}
// Load the datasource schema so we can determine column types // Load the datasource schema so we can determine column types
const fetchSchema = async dataSource => { const fetchSchema = async dataSource => {
if (dataSource) { if (dataSource) {
@ -109,7 +68,7 @@
<BlockComponent <BlockComponent
type="form" type="form"
bind:id={formId} bind:id={formId}
props={{ dataSource, disableValidation: true }} props={{ dataSource, disableValidation: true, editAutoColumns: true }}
> >
{#if title || enrichedSearchColumns?.length || showTitleButton} {#if title || enrichedSearchColumns?.length || showTitleButton}
<div class="header" class:mobile={$context.device.mobile}> <div class="header" class:mobile={$context.device.mobile}>

View File

@ -13,6 +13,10 @@
// for fields rendered in things like search blocks. // for fields rendered in things like search blocks.
export let disableValidation = false export let disableValidation = false
// Not exposed as a builder setting. Used internally to allow searching on
// auto columns.
export let editAutoColumns = false
const context = getContext("context") const context = getContext("context")
const { API, fetchDatasourceSchema } = getContext("sdk") const { API, fetchDatasourceSchema } = getContext("sdk")
@ -107,6 +111,7 @@
{table} {table}
{initialValues} {initialValues}
{disableValidation} {disableValidation}
{editAutoColumns}
> >
<slot /> <slot />
</InnerForm> </InnerForm>

View File

@ -11,6 +11,7 @@
export let schema export let schema
export let table export let table
export let disableValidation = false export let disableValidation = false
export let editAutoColumns = false
const component = getContext("component") const component = getContext("component")
const { styleable, Provider, ActionTypes } = getContext("sdk") const { styleable, Provider, ActionTypes } = getContext("sdk")
@ -183,7 +184,8 @@
fieldId, fieldId,
value: initialValue, value: initialValue,
error: initialError, error: initialError,
disabled: disabled || fieldDisabled || isAutoColumn, disabled:
disabled || fieldDisabled || (isAutoColumn && !editAutoColumns),
defaultValue, defaultValue,
validator, validator,
lastUpdate: Date.now(), lastUpdate: Date.now(),

View File

@ -0,0 +1,81 @@
import { makePropSafe as safe } from "@budibase/string-templates"
// Map of data types to component types for search fields inside blocks
const schemaComponentMap = {
string: "stringfield",
options: "optionsfield",
number: "numberfield",
datetime: "datetimefield",
boolean: "booleanfield",
formula: "stringfield",
}
/**
* Determine data types for search fields and only use those that are valid
* @param searchColumns the search columns to use
* @param schema the data source schema
*/
export const enrichSearchColumns = (searchColumns, schema) => {
let enrichedColumns = []
searchColumns?.forEach(column => {
const schemaType = schema?.[column]?.type
const componentType = schemaComponentMap[schemaType]
if (componentType) {
enrichedColumns.push({
name: column,
componentType,
type: schemaType,
})
}
})
return enrichedColumns.slice(0, 5)
}
/**
* Enriches a normal datasource filter with bindings representing the additional
* search fields configured as part of a searchable block. These bindings are
* fields inside a form used as part of the block.
* @param filter the normal data provider filter
* @param columns the enriched search column structure
* @param formId the ID of the form containing the search fields
*/
export const enrichFilter = (filter, columns, formId) => {
let enrichedFilter = [...(filter || [])]
columns?.forEach(column => {
const safePath = column.name.split(".").map(safe).join(".")
const stringType = column.type === "string" || column.type === "formula"
const dateType = column.type === "datetime"
const binding = `${safe(formId)}.${safePath}`
// For dates, use a range of the entire day selected
if (dateType) {
enrichedFilter.push({
field: column.name,
type: column.type,
operator: "rangeLow",
valueType: "Binding",
value: `{{ ${binding} }}`,
})
const format = "YYYY-MM-DDTHH:mm:ss.SSSZ"
enrichedFilter.push({
field: column.name,
type: column.type,
operator: "rangeHigh",
valueType: "Binding",
value: `{{ date (add (date ${binding} "x") 86399999) "${format}" }}`,
})
}
// For other fields, do an exact match
else {
enrichedFilter.push({
field: column.name,
type: column.type,
operator: stringType ? "string" : "equal",
valueType: "Binding",
value: `{{ ${binding} }}`,
})
}
})
return enrichedFilter
}

View File

@ -99,8 +99,16 @@ export const buildLuceneQuery = filter => {
filter.forEach(expression => { filter.forEach(expression => {
let { operator, field, type, value, externalType } = expression let { operator, field, type, value, externalType } = expression
// Parse all values into correct types // Parse all values into correct types
if (type === "datetime" && value) { if (type === "datetime") {
// Ensure date value is a valid date and parse into correct format
if (!value) {
return
}
try {
value = new Date(value).toISOString() value = new Date(value).toISOString()
} catch (error) {
return
}
} }
if (type === "number" && !Array.isArray(value)) { if (type === "number" && !Array.isArray(value)) {
if (operator === "oneOf") { if (operator === "oneOf") {