Skip to content

Commit

Permalink
feat(UI): multi column table filters
Browse files Browse the repository at this point in the history
  • Loading branch information
toriphes committed Oct 17, 2021
1 parent adf407c commit 0e15c39
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 5 deletions.
5 changes: 4 additions & 1 deletion src/main/ipc-handlers/tables.js
Expand Up @@ -16,7 +16,7 @@ export default (connections) => {
}
});

ipcMain.handle('get-table-data', async (event, { uid, schema, table, limit, page, sortParams }) => {
ipcMain.handle('get-table-data', async (event, { uid, schema, table, limit, page, sortParams, where }) => {
try {
const offset = (page - 1) * limit;
const query = connections[uid]
Expand All @@ -29,6 +29,9 @@ export default (connections) => {
if (sortParams && sortParams.field && sortParams.dir)
query.orderBy({ [sortParams.field]: sortParams.dir.toUpperCase() });

if (where)
query.where(where);

const result = await query.run({ details: true, schema });

return { status: 'success', response: result };
Expand Down
33 changes: 31 additions & 2 deletions src/renderer/components/WorkspaceTabTable.vue
Expand Up @@ -71,6 +71,13 @@

<div class="divider-vert py-3" />

<button
class="btn btn-sm"
:class="{'btn-primary': isSearch, 'btn-dark': !isSearch}"
@click="isSearch = !isSearch"
>
<i class="mdi mdi-24px mdi-magnify" />
</button>
<button
v-if="isTable"
class="btn btn-dark btn-sm"
Expand Down Expand Up @@ -121,6 +128,11 @@
</div>
</div>
</div>
<WorkspaceTabTableFilters
v-if="isSearch"
:fields="fields"
@filter="updateFilters"
/>
<div class="workspace-query-results p-relative column col-12">
<BaseLoader v-if="isQuering" />
<WorkspaceTabQueryTable
Expand Down Expand Up @@ -160,6 +172,7 @@
import Tables from '@/ipc-api/Tables';
import BaseLoader from '@/components/BaseLoader';
import WorkspaceTabQueryTable from '@/components/WorkspaceTabQueryTable';
import WorkspaceTabTableFilters from '@/components/WorkspaceTabTableFilters';
import ModalNewTableRow from '@/components/ModalNewTableRow';
import ModalFakerRows from '@/components/ModalFakerRows';
import { mapGetters, mapActions } from 'vuex';
Expand All @@ -170,6 +183,7 @@ export default {
components: {
BaseLoader,
WorkspaceTabQueryTable,
WorkspaceTabTableFilters,
ModalNewTableRow,
ModalFakerRows
},
Expand All @@ -192,13 +206,15 @@ export default {
tabUid: 'data', // ???
isQuering: false,
isPageMenu: false,
isSearch: false,
results: [],
lastTable: null,
isAddModal: false,
isFakerModal: false,
autorefreshTimer: 0,
refreshInterval: null,
sortParams: {},
filters: [],
page: 1,
pageProxy: 1,
approximateCount: 0
Expand Down Expand Up @@ -271,6 +287,12 @@ export default {
if (this.lastTable !== this.table)
this.getTableData();
}
},
isSearch (val) {
if (this.filters.length > 0 && !val) {
this.filters = [];
this.getTableData();
}
}
},
created () {
Expand Down Expand Up @@ -302,7 +324,8 @@ export default {
table: this.table,
limit: this.limit,
page: this.page,
sortParams: this.sortParams
sortParams: this.sortParams,
where: this.filters || []
};
try { // Table data
Expand Down Expand Up @@ -389,11 +412,13 @@ export default {
if (e.key === 'F5')
this.reloadTable();
if (e.ctrlKey) {
if (e.ctrlKey || e.metaKey) {
if (e.key === 'ArrowRight')
this.pageChange('next');
if (e.key === 'ArrowLeft')
this.pageChange('prev');
if (e.keyCode === 70) // f
this.isSearch = !this.isSearch;
}
}
},
Expand All @@ -410,6 +435,10 @@ export default {
},
downloadTable (format) {
this.$refs.queryTable.downloadTable(format, this.table);
},
updateFilters (clausoles) {
this.filters = clausoles;
this.getTableData();
}
}
};
Expand Down
162 changes: 162 additions & 0 deletions src/renderer/components/WorkspaceTabTableFilters.vue
@@ -0,0 +1,162 @@
<template>
<form class="workspace-table-filters" @submit.prevent="doFilter">
<div
v-for="(row, index) of rows"
:key="index"
class="workspace-table-filters-row"
>
<label class="form-checkbox my-0" :title="$t('word.insert')">
<input
v-model="row.active"
type="checkbox"
@change="doFilter"
><i class="form-icon" />
</label>
<select v-model="row.field" class="form-select col-auto select-sm">
<option
v-for="(item, j) of fields"
:key="j"
:value="item.name"
>
{{ item.name }}
</option>
</select>
<select v-model="row.op" class="form-select ml-2 col-auto select-sm">
<option
v-for="(operator, k) of operators"
:key="k"
:value="operator"
>
{{ operator }}
</option>
</select>
<div class="workspace-table-filters-row-value ml-2">
<input
v-if="!row.op.includes('NULL')"
v-model="row.value"
type="text"
class="form-input input-sm"
>
<input
v-if="row.op === 'BETWEEN'"
v-model="row.value2"
type="text"
class="form-input ml-2 input-sm"
>
</div>
<button
class="btn btn-sm btn-dark mr-0 ml-2"
type="button"
@click="addRow"
>
<i class="mdi mdi-plus-circle-outline" />
</button>
<button
class="btn btn-sm btn-dark mr-0 ml-2"
type="button"
@click="removeRow(index)"
>
<i class="mdi mdi-minus-circle-outline" />
</button>
</div>
<div class="workspace-table-filters-buttons">
<button
class="btn btn-sm btn-primary mr-0 ml-2"
type="submit"
>
{{ $t('word.filter') }}
</button>
</div>
</form>
</template>

<script>
export default {
props: {
fields: Array
},
data () {
return {
rows: [],
operators: [
'=', '!=', '>', '<', '>=', '<=', 'IN', 'NOT IN', 'LIKE', 'BETWEEN', 'IS NULL', 'IS NOT NULL'
]
};
},
created () {
this.addRow();
},
methods: {
addRow () {
this.rows.push({ active: true, field: this.fields[0].name, op: '=', value: '', value2: '' });
},
removeRow (i) {
if (this.rows.length >= 2)
this.rows = this.rows.filter((_, idx) => idx !== i);
},
doFilter () {
const clausoles = this.rows.filter(el => el.active).map(el => this.createClausole(el));
this.$emit('filter', clausoles);
},
createClausole (filter) {
const field = this.fields.find(field => field.name === filter.field);
const isNumeric = field.type.match(/INT|FLOAT|DECIMAL/);
let value = null;
switch (filter.op) {
case '=':
case '!=':
value = isNumeric ? filter.value : '"' + filter.value + '"';
break;
case 'BETWEEN':
value = isNumeric ? filter.value : '"' + filter.value + '"';
value += ' AND ';
value += isNumeric ? filter.value2 : '"' + filter.value2 + '"';
console.log(value);
break;
case 'IN':
case 'NOT IN':
value = filter.value.split(',').map(val => {
val = val.trim();
return isNumeric ? val : '"' + val + '"';
}).join(',');
value = '(' + filter.value + ')';
break;
case 'IS NULL':
case 'IS NOT NULL':
value = '';
break;
default:
value = '"' + filter.value + '"';
}
return `${filter.field} ${filter.op} ${value}`;
}
}
};
</script>

<style lang="scss">
.workspace-table-filters {
padding: 0 0.6rem;
width: 100%;
}
.workspace-table-filters-buttons {
display: flex;
flex-direction: row-reverse;
padding-bottom: 0.4rem;
}
.workspace-table-filters-row {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 0.4rem;
}
.workspace-table-filters-row-value {
width: 100%;
display: flex;
}
</style>
3 changes: 2 additions & 1 deletion src/renderer/i18n/en-US.js
Expand Up @@ -120,7 +120,8 @@ module.exports = {
new: 'New',
history: 'History',
select: 'Select',
passphrase: 'Passphrase'
passphrase: 'Passphrase',
filter: 'Filter'
},
message: {
appWelcome: 'Welcome to Antares SQL Client!',
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/i18n/it-IT.js
Expand Up @@ -115,7 +115,8 @@ module.exports = {
cell: 'Cella | Celle',
triggerFunction: 'Funzione di trigger | Funzioni di trigger',
all: 'Tutto',
duplicate: 'Duplica'
duplicate: 'Duplica',
filter: 'Filtra'
},
message: {
appWelcome: 'Benvenuto in Antares SQL Client!',
Expand Down

0 comments on commit 0e15c39

Please sign in to comment.