diff --git a/client/web/compose/src/components/Common/RecordListFilter.vue b/client/web/compose/src/components/Common/RecordListFilter.vue index 037465133..67a30d715 100644 --- a/client/web/compose/src/components/Common/RecordListFilter.vue +++ b/client/web/compose/src/components/Common/RecordListFilter.vue @@ -86,20 +86,48 @@ v-model="filter.operator" :options="getOperators(filter.kind, getField(filter.name))" class="d-flex field-operator w-100" + @change="updateFilterProperties(filter)" /> - + + + { f.record = new compose.Record(this.mock.module, {}) - // If its a system field add value to root of record - if (Object.keys(f.record).includes(f.name)) { + if (this.isBetweenOperator(f.operator)) { + if (this.getField(f.name).isSystem) { + f.record[`${f.name}-start`] = value.start + f.record[`${f.name}-end`] = value.end + } else { + f.record.values[`${f.name}-start`] = value.start + f.record.values[`${f.name}-end`] = value.end + } + + const field = this.mock.module.fields.find(field => field.name === f.name) + + this.mock.module.fields.push({ ...field, name: `${f.name}-end` }) + this.mock.module.fields.push({ ...field, name: `${f.name}-start` }) + } else if (Object.keys(f.record).includes(f.name)) { + // If its a system field add value to root of record f.record[f.name] = value } else { f.record.values[f.name] = value @@ -531,12 +583,35 @@ export default { f.value = record[f.name] || record.values[f.name] } + if (this.isBetweenOperator(f.operator)) { + f.value = { + start: this.getField(f.name).isSystem ? record[`${f.name}-start`] : record.values[`${f.name}-start`], + end: this.getField(f.name).isSystem ? record[`${f.name}-end`] : record.values[`${f.name}-end`], + } + } + return f }) return { groupCondition, filter } })) }, + + updateFilterProperties (filter) { + if (this.isBetweenOperator(filter.operator)) { + filter.record.values[`${filter.name}-start`] = filter.record.values[`${filter.name}-start`] + filter.record.values[`${filter.name}-end`] = filter.record.values[`${filter.name}-end`] + + const field = this.mock.module.fields.find(f => f.name === filter.name) + + this.mock.module.fields.push({ ...field, name: `${filter.name}-end` }) + this.mock.module.fields.push({ ...field, name: `${filter.name}-start` }) + } + }, + + isBetweenOperator (op) { + return ['BETWEEN', 'NOT BETWEEN'].includes(op) + }, }, } diff --git a/client/web/compose/src/lib/record-filter.js b/client/web/compose/src/lib/record-filter.js index 1a4261900..590afc0d1 100644 --- a/client/web/compose/src/lib/record-filter.js +++ b/client/web/compose/src/lib/record-filter.js @@ -16,6 +16,7 @@ export function getRecordListFilterSql (filter) { } const fieldFilter = getFieldFilter(f.name, f.kind, f.value, f.operator) + if (fieldFilter) { query += getFieldFilter(f.name, f.kind, f.value, f.operator) existsPreviousElement = true @@ -28,9 +29,13 @@ export function getRecordListFilterSql (filter) { // Helper function that creates a query for a specific field kind export function getFieldFilter (name, kind, query = '', operator = '=') { - const boolQuery = toBoolean(query) - const numQuery = Number.parseFloat(query) + let boolQuery = query + let numQuery = query + if (typeof query === 'object' && query !== null) { + boolQuery = toBoolean(query) + numQuery = Number.parseFloat(query) + } const build = (op, left, right) => { switch (op.toUpperCase()) { case '!=': @@ -39,7 +44,10 @@ export function getFieldFilter (name, kind, query = '', operator = '=') { case 'IN': case 'NOT IN': // flip left/right for IN/NOT IN - return `${right} ${op} ${left}` + return `'${right}' ${op} ${left}` + case 'BETWEEN': + case 'NOT BETWEEN': + return `${name} ${op} ${query.start} ${query.end}` default: return `${left} ${op} ${right}` } @@ -68,23 +76,43 @@ export function getFieldFilter (name, kind, query = '', operator = '=') { return undefined } - if (['Number'].includes(kind) && !isNaN(numQuery)) { - return build(operator, name, `'${numQuery}'`) + if (['Number'].includes(kind)) { + if (['BETWEEN', 'NOT BETWEEN'].includes(operator)) { + return build(operator, name, numQuery) + } else if (!isNaN(numQuery)) { + return build(operator, name, numQuery) + } } if (['DateTime'].includes(kind)) { - // Build different querries if date, time or datetime - const date = moment(query, ['YYYY-MM-DDTHH:mm:ssZ', 'YYYY-MM-DD']) - const time = moment(query, ['HH:mm']) + if (['BETWEEN', 'NOT BETWEEN'].includes(operator)) { + const startDate = moment(query.start, ['YYYY-MM-DDTHH:mm:ssZ', 'YYYY-MM-DD']) + const endDate = moment(query.end, ['YYYY-MM-DDTHH:mm:ssZ', 'YYYY-MM-DD']) - // @note tweaking the template a bit: - // * adding %f to include fractions; mysql sometimes forces them when formatting date - // * changing Z to +00:00 - // * doing the same for time-only fields - if (date.isValid()) { - return `TIMESTAMP(DATE_FORMAT(${name}, '%Y-%m-%dT%H:%i:00.%f+00:00')) ${operator} TIMESTAMP(DATE_FORMAT('${date.format()}', '%Y-%m-%dT%H:%i:00.%f+00:00'))` - } else if (time.isValid()) { - return `TIME(DATE_FORMAT(${name}, '%Y-%m-%dT%H:%i:00.%f+00:00')) ${operator} TIME('${query}')` + const startTime = moment(query.start, ['HH:mm']) + const endTime = moment(query.end, ['HH:mm']) + + const dataFmtEntry = (date) => `TIMESTAMP(DATE_FORMAT('${date.format()}', '%Y-%m-%dT%H:%i:00.%f+00:00'))` + + if (startDate.isValid() && endDate.isValid()) { + return `TIMESTAMP(DATE_FORMAT(${name}, '%Y-%m-%dT%H:%i:00.%f+00:00')) ${operator} ${dataFmtEntry(startDate)} ${dataFmtEntry(endDate)}` + } else if (startTime.isValid() && endTime.isValid()) { + return `TIME(DATE_FORMAT(${name}, '%Y-%m-%dT%H:%i:00.%f+00:00')) ${operator} TIME('${query.start}') TIME('${query.end}')` + } + } else { + // Build different querries if date, time or datetime + const date = moment(query, ['YYYY-MM-DDTHH:mm:ssZ', 'YYYY-MM-DD']) + const time = moment(query, ['HH:mm']) + + // @note tweaking the template a bit: + // * adding %f to include fractions; mysql sometimes forces them when formatting date + // * changing Z to +00:00 + // * doing the same for time-only fields + if (date.isValid()) { + return `TIMESTAMP(DATE_FORMAT(${name}, '%Y-%m-%dT%H:%i:00.%f+00:00')) ${operator} TIMESTAMP(DATE_FORMAT('${date.format()}', '%Y-%m-%dT%H:%i:00.%f+00:00'))` + } else if (time.isValid()) { + return `TIME(DATE_FORMAT(${name}, '%Y-%m-%dT%H:%i:00.%f+00:00')) ${operator} TIME('${query}')` + } } } diff --git a/locale/en/corteza-webapp-compose/block.yaml b/locale/en/corteza-webapp-compose/block.yaml index 63c3ce4c0..4598f6877 100644 --- a/locale/en/corteza-webapp-compose/block.yaml +++ b/locale/en/corteza-webapp-compose/block.yaml @@ -138,6 +138,7 @@ general: save: Save search: Search magnify: Magnify Block + and: And module: Module style: danger: Danger variant @@ -281,6 +282,8 @@ recordList: greaterThan: Greater than lessThan: Less than notEqual: Not equal + between: Between + notBetween: Not between title: Record list filter update: Update filter where: Where