search UI complete, server side cursor based pagination
This commit is contained in:
parent
bfa865cf3c
commit
07aeccb36d
|
@ -46,6 +46,11 @@
|
||||||
innerVal = value.target.value
|
innerVal = value.target.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === "number") {
|
||||||
|
innerVal = parseInt(innerVal)
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof innerVal === "string") {
|
if (typeof innerVal === "string") {
|
||||||
onChange(replaceBindings(innerVal))
|
onChange(replaceBindings(innerVal))
|
||||||
} else {
|
} else {
|
||||||
|
@ -72,6 +77,7 @@
|
||||||
value={safeValue}
|
value={safeValue}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
{type}
|
||||||
{...props}
|
{...props}
|
||||||
name={key} />
|
name={key} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,8 +5,8 @@ import { fetchRelationshipData } from "./relationships"
|
||||||
import { executeQuery } from "./queries"
|
import { executeQuery } from "./queries"
|
||||||
import { enrichRows } from "./rows"
|
import { enrichRows } from "./rows"
|
||||||
|
|
||||||
export const searchTable = async ({ tableId, search, page, pageSize }) => {
|
export const searchTable = async ({ tableId, search, pagination }) => {
|
||||||
const rows = await searchTableData({ tableId, search, page, pageSize })
|
const rows = await searchTableData({ tableId, search, pagination })
|
||||||
return rows
|
return rows
|
||||||
// TODO
|
// TODO
|
||||||
// Enrich rows so they can displayed properly
|
// Enrich rows so they can displayed properly
|
||||||
|
|
|
@ -22,18 +22,12 @@ export const fetchTableData = async tableId => {
|
||||||
* @param {String} tableId - id of the table to search
|
* @param {String} tableId - id of the table to search
|
||||||
* @param {Object} search - Mango Compliant search object
|
* @param {Object} search - Mango Compliant search object
|
||||||
*/
|
*/
|
||||||
export const searchTableData = async ({
|
export const searchTableData = async ({ tableId, search, pagination }) => {
|
||||||
tableId,
|
|
||||||
search,
|
|
||||||
cursor,
|
|
||||||
pageSize,
|
|
||||||
}) => {
|
|
||||||
const rows = await API.post({
|
const rows = await API.post({
|
||||||
url: `/api/${tableId}/rows/search`,
|
url: `/api/${tableId}/rows/search`,
|
||||||
body: {
|
body: {
|
||||||
query: search,
|
query: search,
|
||||||
pageSize,
|
pagination,
|
||||||
cursor,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return await enrichRows(rows, tableId)
|
return await enrichRows(rows, tableId)
|
||||||
|
|
|
@ -231,7 +231,7 @@ exports.fetchView = async function(ctx) {
|
||||||
|
|
||||||
exports.search = async function(ctx) {
|
exports.search = async function(ctx) {
|
||||||
// const appId = ctx.user.appId
|
// const appId = ctx.user.appId
|
||||||
const appId = 'app_5aa5e9cf26694f9ea02f054050d7ae63'
|
const appId = "app_1987903cf3604d459969c80cf17651a0"
|
||||||
|
|
||||||
// const { pageSize = 10, cursor } = ctx.query
|
// const { pageSize = 10, cursor } = ctx.query
|
||||||
|
|
||||||
|
@ -245,7 +245,10 @@ exports.search = async function(ctx) {
|
||||||
|
|
||||||
const db = new CouchDB(appId)
|
const db = new CouchDB(appId)
|
||||||
|
|
||||||
const { query, pageSize = 10, cursor } = ctx.request.body
|
const {
|
||||||
|
query,
|
||||||
|
pagination: { pageSize = 10, cursor, reverse },
|
||||||
|
} = ctx.request.body
|
||||||
|
|
||||||
query.tableId = ctx.params.tableId
|
query.tableId = ctx.params.tableId
|
||||||
|
|
||||||
|
@ -257,13 +260,12 @@ exports.search = async function(ctx) {
|
||||||
const response = await db.find({
|
const response = await db.find({
|
||||||
selector: query,
|
selector: query,
|
||||||
limit: pageSize,
|
limit: pageSize,
|
||||||
// sort: ["_id"],
|
sort: ["_id"],
|
||||||
|
skip: 1,
|
||||||
})
|
})
|
||||||
ctx.body = response.docs
|
const rows = response.docs
|
||||||
|
|
||||||
// TODO: probably attach relationships
|
ctx.body = await linkRows.attachLinkInfo(appId, rows)
|
||||||
// const rows = response.docs.map(row => row.doc)
|
|
||||||
// ctx.body = await linkRows.attachLinkInfo(appId, rows)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetchTableRows = async function(ctx) {
|
exports.fetchTableRows = async function(ctx) {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -135,12 +135,12 @@
|
||||||
{
|
{
|
||||||
"type": "multifield",
|
"type": "multifield",
|
||||||
"label": "Columns",
|
"label": "Columns",
|
||||||
"key": "valueColumns",
|
"key": "columns",
|
||||||
"dependsOn": "table"
|
"dependsOn": "table"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"label": "Result Page Size",
|
"label": "Rows Per Page",
|
||||||
"key": "pageSize"
|
"key": "pageSize"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
|
const ENTER_KEY = 13
|
||||||
|
|
||||||
const { authStore, styleable } = getContext("sdk")
|
const { authStore, styleable } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
|
||||||
|
@ -29,7 +31,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeydown(evt) {
|
function handleKeydown(evt) {
|
||||||
console.log(evt.keyCode)
|
if (evt.keyCode === ENTER_KEY) {
|
||||||
|
login()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -16,68 +16,97 @@
|
||||||
export let table = []
|
export let table = []
|
||||||
export let columns = []
|
export let columns = []
|
||||||
export let pageSize = 50
|
export let pageSize = 50
|
||||||
export let noRowsMessage = "Feed me some data"
|
export let noRowsMessage = "No Rows"
|
||||||
|
|
||||||
let rows = []
|
let rows = []
|
||||||
let loaded = false
|
let loaded = false
|
||||||
// let searchableFields = []
|
|
||||||
let search = {}
|
let search = {}
|
||||||
let tableDefinition
|
let tableDefinition
|
||||||
let schema = {}
|
let schema
|
||||||
let page = 1
|
|
||||||
|
|
||||||
$: columns = Object.keys(schema).filter(key => schema[key].searchable)
|
// pagination
|
||||||
$: cursor = rows[rows.length - 1]?._id
|
let pagination = {
|
||||||
$: page && fetchData(table)
|
page: 1
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchData(table) {
|
$: fetchData(table, pagination)
|
||||||
|
// omit empty strings
|
||||||
|
$: parsedSearch = Object.keys(search).reduce((acc, next) => search[next] ? { ...acc, [next]: search[next] } : acc, {})
|
||||||
|
|
||||||
|
async function fetchData(table, pagination) {
|
||||||
if (!isEmpty(table)) {
|
if (!isEmpty(table)) {
|
||||||
const tableDef = await API.fetchTableDefinition(table)
|
const tableDef = await API.fetchTableDefinition(table)
|
||||||
schema = tableDef.schema
|
schema = tableDef.schema
|
||||||
rows = await API.searchTable({
|
rows = await API.searchTable({
|
||||||
tableId: table,
|
tableId: table,
|
||||||
search,
|
search: parsedSearch,
|
||||||
pageSize,
|
pagination: {
|
||||||
cursor,
|
pageSize,
|
||||||
|
...pagination
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
loaded = true
|
loaded = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function nextPage() {
|
||||||
|
// set cursor to last element
|
||||||
|
pagination = {
|
||||||
|
// lastCursor: rows[0],
|
||||||
|
cursor: rows[rows.length - 1]?._id,
|
||||||
|
reverse: true,
|
||||||
|
page: pagination.page += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: implement
|
||||||
|
function previousPage() {
|
||||||
|
pagination = {
|
||||||
|
cursor: lastCursor,
|
||||||
|
reverse: true,
|
||||||
|
page: pagination.page - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div use:styleable={$component.styles}>
|
<div use:styleable={$component.styles}>
|
||||||
<div class="query-builder">
|
<div class="query-builder">
|
||||||
{#each columns as field}
|
{#if schema}
|
||||||
<div class="form-field">
|
{#each columns as field}
|
||||||
<Label extraSmall grey>{schema[field].name}</Label>
|
<div class="form-field">
|
||||||
{#if schema[field].type === 'options'}
|
<Label extraSmall grey>{schema[field].name}</Label>
|
||||||
<Select secondary bind:value={search[field]}>
|
{#if schema[field].type === 'options'}
|
||||||
<option value="">Choose an option</option>
|
<Select secondary bind:value={search[field]}>
|
||||||
{#each schema[field].constraints.inclusion as opt}
|
<option value="">Choose an option</option>
|
||||||
<option>{opt}</option>
|
{#each schema[field].constraints.inclusion as opt}
|
||||||
{/each}
|
<option>{opt}</option>
|
||||||
</Select>
|
{/each}
|
||||||
{:else if schema[field].type === 'datetime'}
|
</Select>
|
||||||
<DatePicker bind:value={search[field]} />
|
{:else if schema[field].type === 'datetime'}
|
||||||
{:else if schema[field].type === 'boolean'}
|
<DatePicker bind:value={search[field]} />
|
||||||
<Toggle text={schema[field].name} bind:checked={search[field]} />
|
{:else if schema[field].type === 'boolean'}
|
||||||
{:else if schema[field].type === 'number'}
|
<Toggle text={schema[field].name} bind:checked={search[field]} />
|
||||||
<Input type="number" bind:value={search[field]} />
|
{:else if schema[field].type === 'number'}
|
||||||
{:else if schema[field].type === 'string'}
|
<Input type="number" bind:value={search[field]} />
|
||||||
<Input bind:value={search[field]} />
|
{:else if schema[field].type === 'string'}
|
||||||
{/if}
|
<Input bind:value={search[field]} />
|
||||||
</div>
|
{/if}
|
||||||
{/each}
|
</div>
|
||||||
<Button blue on:click={() => fetchData(table)}>Search</Button>
|
{/each}
|
||||||
<Button
|
{/if}
|
||||||
red
|
<div class="actions">
|
||||||
on:click={() => {
|
<Button
|
||||||
search = {}
|
secondary
|
||||||
fetchData(table)
|
on:click={() => {
|
||||||
}}>
|
search = {}
|
||||||
Reset
|
pagination = {
|
||||||
</Button>
|
page: 1
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
|
<Button primary on:click={() => fetchData(table)}>Search</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if rows.length > 0}
|
{#if rows.length > 0}
|
||||||
{#if $component.children === 0 && $builderStore.inBuilder}
|
{#if $component.children === 0 && $builderStore.inBuilder}
|
||||||
|
@ -90,13 +119,17 @@
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
{:else if loaded && $builderStore.inBuilder}
|
{:else if loaded && $builderStore.inBuilder}
|
||||||
|
<p>Feed me some data</p>
|
||||||
|
{:else}
|
||||||
<p>{noRowsMessage}</p>
|
<p>{noRowsMessage}</p>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="pagination">
|
<div class="pagination">
|
||||||
{#if page > 1}
|
<!-- {#if pagination.page > 1}
|
||||||
<Button blue on:click={() => (page -= 1)}>Back</Button>
|
<Button blue on:click={previousPage}>Back</Button>
|
||||||
|
{/if} -->
|
||||||
|
{#if rows.length === pageSize}
|
||||||
|
<Button primary on:click={nextPage}>Next</Button>
|
||||||
{/if}
|
{/if}
|
||||||
<Button blue on:click={() => (page += 1)}>Next</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -115,6 +148,13 @@
|
||||||
border-radius: var(--border-radius-s);
|
border-radius: var(--border-radius-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: var(--spacing-s);
|
||||||
|
justify-content: flex-end;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
}
|
||||||
|
|
||||||
.form-field {
|
.form-field {
|
||||||
margin-bottom: var(--spacing-m);
|
margin-bottom: var(--spacing-m);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue