Bindings support for views and table row searches
This commit is contained in:
parent
5614c040ea
commit
6bbdf0e474
|
@ -1,7 +1,9 @@
|
|||
<script>
|
||||
import { createEventDispatcher } from "svelte"
|
||||
import { ActionButton, Modal, ModalContent } from "@budibase/bbui"
|
||||
import { ActionButton, Drawer, Button } from "@budibase/bbui"
|
||||
import FilterDrawer from "components/design/settings/controls/FilterEditor/FilterDrawer.svelte"
|
||||
import { getUserBindings } from "dataBinding"
|
||||
import { makePropSafe } from "@budibase/string-templates"
|
||||
|
||||
export let schema
|
||||
export let filters
|
||||
|
@ -10,7 +12,7 @@
|
|||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let modal
|
||||
let drawer
|
||||
|
||||
$: tempValue = filters || []
|
||||
$: schemaFields = Object.entries(schema || {}).map(
|
||||
|
@ -22,37 +24,52 @@
|
|||
|
||||
$: text = getText(filters)
|
||||
$: selected = tempValue.filter(x => !x.onEmptyFilter)?.length > 0
|
||||
|
||||
$: bindings = [
|
||||
{
|
||||
type: "context",
|
||||
runtimeBinding: `${makePropSafe("now")}`,
|
||||
readableBinding: `Date`,
|
||||
category: "Date",
|
||||
icon: "Date",
|
||||
display: {
|
||||
name: "Server date",
|
||||
},
|
||||
},
|
||||
...getUserBindings(true),
|
||||
]
|
||||
const getText = filters => {
|
||||
const count = filters?.filter(filter => filter.field)?.length
|
||||
return count ? `Filter (${count})` : "Filter"
|
||||
}
|
||||
</script>
|
||||
|
||||
<ActionButton icon="Filter" quiet {disabled} on:click={modal.show} {selected}>
|
||||
<ActionButton icon="Filter" quiet {disabled} on:click={drawer.show} {selected}>
|
||||
{text}
|
||||
</ActionButton>
|
||||
<Modal bind:this={modal}>
|
||||
<ModalContent
|
||||
title="Filter"
|
||||
confirmText="Save"
|
||||
size="XL"
|
||||
onConfirm={() => dispatch("change", tempValue)}
|
||||
>
|
||||
<div class="wrapper">
|
||||
<FilterDrawer
|
||||
allowBindings={false}
|
||||
{filters}
|
||||
{schemaFields}
|
||||
datasource={{ type: "table", tableId }}
|
||||
on:change={e => (tempValue = e.detail)}
|
||||
/>
|
||||
</div>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
<style>
|
||||
.wrapper :global(.main) {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
<Drawer
|
||||
bind:this={drawer}
|
||||
title="Filtering"
|
||||
on:drawerHide
|
||||
on:drawerShow
|
||||
forceModal
|
||||
>
|
||||
<Button
|
||||
cta
|
||||
slot="buttons"
|
||||
on:click={() => {
|
||||
dispatch("change", tempValue)
|
||||
drawer.hide()
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<FilterDrawer
|
||||
slot="body"
|
||||
{filters}
|
||||
{schemaFields}
|
||||
datasource={{ type: "table", tableId }}
|
||||
on:change={e => (tempValue = e.detail)}
|
||||
{bindings}
|
||||
/>
|
||||
</Drawer>
|
||||
|
|
|
@ -304,6 +304,7 @@
|
|||
OperatorOptions.ContainsAny.value,
|
||||
].includes(filter.operator)}
|
||||
disabled={filter.noValue}
|
||||
type={filter.valueType}
|
||||
/>
|
||||
{:else}
|
||||
<DrawerBindableInput disabled />
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script>
|
||||
import { Select, Multiselect } from "@budibase/bbui"
|
||||
import { fetchData } from "@budibase/frontend-core"
|
||||
|
||||
import { API } from "api"
|
||||
|
||||
export let value = null
|
||||
|
@ -23,12 +22,14 @@
|
|||
$: component = multiselect ? Multiselect : Select
|
||||
</script>
|
||||
|
||||
<svelte:component
|
||||
this={component}
|
||||
bind:value
|
||||
autocomplete
|
||||
{options}
|
||||
getOptionLabel={option => option.email}
|
||||
getOptionValue={option => option._id}
|
||||
{disabled}
|
||||
/>
|
||||
<div class="user-control">
|
||||
<svelte:component
|
||||
this={component}
|
||||
bind:value
|
||||
autocomplete
|
||||
{options}
|
||||
getOptionLabel={option => option.email}
|
||||
getOptionValue={option => option._id}
|
||||
{disabled}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@ import stream from "stream"
|
|||
import archiver from "archiver"
|
||||
|
||||
import { quotas } from "@budibase/pro"
|
||||
import { objectStore } from "@budibase/backend-core"
|
||||
import { objectStore, context } from "@budibase/backend-core"
|
||||
import * as internal from "./internal"
|
||||
import * as external from "./external"
|
||||
import { isExternalTableID } from "../../../integrations/utils"
|
||||
|
@ -198,8 +198,21 @@ export async function destroy(ctx: UserCtx<DeleteRowRequest>) {
|
|||
export async function search(ctx: Ctx<SearchRowRequest, SearchRowResponse>) {
|
||||
const tableId = utils.getTableId(ctx)
|
||||
|
||||
// Current user context for bindable search
|
||||
const { _id, _rev, firstName, lastName, email, status, roleId } = ctx.user
|
||||
|
||||
await context.ensureSnippetContext()
|
||||
|
||||
const enrichedQuery = await utils.enrichSearchContext(
|
||||
{ ...ctx.request.body.query },
|
||||
{
|
||||
user: { _id, _rev, firstName, lastName, email, status, roleId },
|
||||
}
|
||||
)
|
||||
|
||||
const searchParams: RowSearchParams = {
|
||||
...ctx.request.body,
|
||||
query: enrichedQuery,
|
||||
tableId,
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ import {
|
|||
FieldType,
|
||||
RelationshipsJson,
|
||||
Row,
|
||||
SearchRowRequest,
|
||||
SearchRowResponse,
|
||||
Table,
|
||||
UserCtx,
|
||||
} from "@budibase/types"
|
||||
|
@ -22,7 +24,7 @@ import {
|
|||
getInternalRowId,
|
||||
} from "./basic"
|
||||
import sdk from "../../../../sdk"
|
||||
|
||||
import { processStringSync } from "@budibase/string-templates"
|
||||
import validateJs from "validate.js"
|
||||
|
||||
validateJs.extend(validateJs.validators.datetime, {
|
||||
|
@ -187,3 +189,40 @@ export async function sqlOutputProcessing(
|
|||
export function isUserMetadataTable(tableId: string) {
|
||||
return tableId === InternalTables.USER_METADATA
|
||||
}
|
||||
|
||||
export async function enrichSearchContext(
|
||||
fields: Record<string, any>,
|
||||
inputs = {},
|
||||
helpers = true
|
||||
): Promise<Record<string, any>> {
|
||||
const enrichedQuery: Record<string, any> = {}
|
||||
if (!fields || !inputs) {
|
||||
return enrichedQuery
|
||||
}
|
||||
const parameters = { ...inputs }
|
||||
// enrich the fields with dynamic parameters
|
||||
for (let key of Object.keys(fields)) {
|
||||
if (fields[key] == null) {
|
||||
continue
|
||||
}
|
||||
if (typeof fields[key] === "object") {
|
||||
// enrich nested fields object
|
||||
enrichedQuery[key] = await enrichSearchContext(
|
||||
fields[key],
|
||||
parameters,
|
||||
helpers
|
||||
)
|
||||
} else if (typeof fields[key] === "string") {
|
||||
// enrich string value as normal
|
||||
enrichedQuery[key] = processStringSync(fields[key], parameters, {
|
||||
noEscaping: true,
|
||||
noHelpers: !helpers,
|
||||
escapeNewlines: true,
|
||||
})
|
||||
} else {
|
||||
enrichedQuery[key] = fields[key]
|
||||
}
|
||||
}
|
||||
|
||||
return enrichedQuery
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@ import {
|
|||
} from "@budibase/types"
|
||||
import { dataFilters } from "@budibase/shared-core"
|
||||
import sdk from "../../../sdk"
|
||||
import { db } from "@budibase/backend-core"
|
||||
import { db, context } from "@budibase/backend-core"
|
||||
import { enrichSearchContext, userSearchFromContext } from "./utils"
|
||||
|
||||
export async function searchView(
|
||||
ctx: UserCtx<SearchViewRowRequest, SearchRowResponse>
|
||||
|
@ -56,10 +57,19 @@ export async function searchView(
|
|||
})
|
||||
}
|
||||
|
||||
// Current user search context.
|
||||
const { _id, _rev, firstName, lastName, email, status, roleId } = ctx.user
|
||||
|
||||
await context.ensureSnippetContext()
|
||||
|
||||
const enrichedQuery = await enrichSearchContext(query, {
|
||||
user: { _id, _rev, firstName, lastName, email, status, roleId },
|
||||
})
|
||||
|
||||
const searchOptions: RequiredKeys<SearchViewRowRequest> &
|
||||
RequiredKeys<Pick<RowSearchParams, "tableId" | "query" | "fields">> = {
|
||||
tableId: view.tableId,
|
||||
query,
|
||||
query: enrichedQuery,
|
||||
fields: viewFields,
|
||||
...getSortOptions(body, view),
|
||||
limit: body.limit,
|
||||
|
|
Loading…
Reference in New Issue