Browse Source

feat: M2M optimizations

Signed-off-by: Pranav C <61551451+pranavxc@users.noreply.github.com>
pull/341/head
Pranav C 3 years ago
parent
commit
e42aea3ec1
  1. 8
      packages/nc-gui/components/ProjectTreeView.vue
  2. 18
      packages/nc-gui/components/project/spreadsheet/components/expandedForm.vue
  3. 1
      packages/nc-gui/components/project/spreadsheet/components/pagination.vue
  4. 2
      packages/nc-gui/components/project/spreadsheet/components/spreadsheetNavDrawer.vue
  5. 20
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listChildItems.vue
  6. 27
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listChildItemsModal.vue
  7. 16
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listItems.vue
  8. 2
      packages/nc-gui/components/project/spreadsheet/mixins/spreadsheet.js
  9. 21
      packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue
  10. 2
      packages/nc-gui/components/sponsorMini.vue
  11. 2
      packages/nc-gui/components/sponsorOverlay.vue
  12. 10
      packages/nc-gui/store/meta.js
  13. 6
      packages/nc-gui/store/sqlMgr.js
  14. 2
      packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts
  15. 12
      packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts

8
packages/nc-gui/components/ProjectTreeView.vue

@ -448,7 +448,7 @@
<!-- <v-tooltip bottom>--> <!-- <v-tooltip bottom>-->
<!-- <template v-slot:activator="{on}">--> <!-- <template v-slot:activator="{on}">-->
<!-- <v-list-item dense v-on="on" @click="openLink('https://github.com/sponsors/xgenecloud')" class="body-2">--> <!-- <v-list-item dense v-on="on" @click="openLink('https://github.com/sponsors/nocodb')" class="body-2">-->
<!-- <v-list-item-icon>--> <!-- <v-list-item-icon>-->
<!-- <v-icon color="red" class=" heart-anim" small> mdi-heart</v-icon>--> <!-- <v-icon color="red" class=" heart-anim" small> mdi-heart</v-icon>-->
<!-- </v-list-item-icon>--> <!-- </v-list-item-icon>-->
@ -515,21 +515,21 @@
<v-icon color="red" class=" heart-anim" small> mdi-heart</v-icon> <v-icon color="red" class=" heart-anim" small> mdi-heart</v-icon>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item dense v-on="on" @click="openLink('https://github.com/sponsors/xgenecloud')" class="body-2"> <v-list-item dense v-on="on" @click="openLink('https://github.com/sponsors/nocodb')" class="body-2">
<v-list-item-icon> <v-list-item-icon>
</v-list-item-icon> </v-list-item-icon>
<v-list-item-title><span class="font-weight-regular caption">Sponsor Us</span></v-list-item-title> <v-list-item-title><span class="font-weight-regular caption">Sponsor Us</span></v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item dense v-on="on" @click="openLink('https://github.com/sponsors/xgenecloud')" class="body-2"> <v-list-item dense v-on="on" @click="openLink('https://github.com/sponsors/nocodb')" class="body-2">
<v-list-item-icon> <v-list-item-icon>
<v-icon color="red" class=" heart-anim" small> mdi-heart</v-icon> <v-icon color="red" class=" heart-anim" small> mdi-heart</v-icon>
</v-list-item-icon> </v-list-item-icon>
<v-list-item-title><span class="font-weight-regular caption">Sponsor Us</span></v-list-item-title> <v-list-item-title><span class="font-weight-regular caption">Sponsor Us</span></v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item dense v-on="on" @click="openLink('https://github.com/sponsors/xgenecloud')" class="body-2"> <v-list-item dense v-on="on" @click="openLink('https://github.com/sponsors/nocodb')" class="body-2">
<v-list-item-icon> <v-list-item-icon>
<v-icon color="red" class=" heart-anim" small> mdi-heart</v-icon> <v-icon color="red" class=" heart-anim" small> mdi-heart</v-icon>
</v-list-item-icon> </v-list-item-icon>

18
packages/nc-gui/components/project/spreadsheet/components/expandedForm.vue

@ -357,7 +357,7 @@ export default {
}, },
async reload() { async reload() {
// const id = this.meta.columns.filter((c) => c.pk).map(c => this.localState[c._cn]).join('___'); // const id = this.meta.columns.filter((c) => c.pk).map(c => this.localState[c._cn]).join('___');
const where = this.meta.columns.filter((c) => c.pk).map(c => `(${c._cn},eq,${this.value[c._cn]})`).join('~and'); const where = this.meta.columns.filter((c) => c.pk).map(c => `(${c._cn},eq,${this.localState[c._cn]})`).join('~and');
this.$set(this, 'changedColumns', {}); this.$set(this, 'changedColumns', {});
// this.localState = await this.api.read(id); // this.localState = await this.api.read(id);
const data = await this.api.list({...(this.queryParams || {}), where}) || [{}]; const data = await this.api.list({...(this.queryParams || {}), where}) || [{}];
@ -452,12 +452,12 @@ export default {
/* todo: refactor */ /* todo: refactor */
.row-col { .row-col {
& > div > input, & > div > input,
//& > div div > input, //& > div div > input,
& > div > .xc-input > input, & > div > .xc-input > input,
& > div > .xc-input >div > input, & > div > .xc-input > div > input,
& > div > select, & > div > select,
& > div > .xc-input > select, & > div > .xc-input > select,
& > div textarea { & > div textarea:not(.inputarea) {
border: 1px solid #7f828b33; border: 1px solid #7f828b33;
padding: 1px 5px; padding: 1px 5px;
font-size: .8rem; font-size: .8rem;
@ -487,10 +487,10 @@ export default {
//& > div div > input, //& > div div > input,
& > div > input, & > div > input,
& > div > .xc-input > input, & > div > .xc-input > input,
& > div > .xc-input >div > input, & > div > .xc-input > div > input,
& > div > select, & > div > select,
& > div > .xc-input > select, & > div > .xc-input > select,
& > div textarea { & > div textarea:not(.inputarea) {
background: #1e1e1e; background: #1e1e1e;
} }
} }
@ -501,12 +501,12 @@ export default {
.row-col { .row-col {
& > div > input, & > div > input,
//& > div div > input, //& > div div > input,
& > div > .xc-input > input, & > div > .xc-input > input,
& > div > .xc-input >div > input, & > div > .xc-input > div > input,
& > div > select, & > div > select,
& > div > .xc-input > select, & > div > .xc-input > select,
& > div textarea { & > div textarea:not(.inputarea) {
background: white; background: white;
} }
} }

1
packages/nc-gui/components/project/spreadsheet/components/pagination.vue

@ -51,5 +51,4 @@ export default {
</script> </script>
<style scoped> <style scoped>
</style> </style>

2
packages/nc-gui/components/project/spreadsheet/components/spreadsheetNavDrawer.vue

@ -264,7 +264,7 @@
<v-hover > <v-hover >
<template v-slot:default="{hover}"> <template v-slot:default="{hover}">
<v-btn <v-btn
:color="hover ?'primary' : 'grey'" class="mb-2" small outlined href="https://github.com/sponsors/xgenecloud" :color="hover ?'primary' : 'grey'" class="mb-2" small outlined href="https://github.com/sponsors/nocodb"
target="_blank"> target="_blank">
<v-icon small color="red" class="mr-2">mdi-heart-outline</v-icon> <v-icon small color="red" class="mr-2">mdi-heart-outline</v-icon>
Sponsor Us Sponsor Us

20
packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listChildItems.vue

@ -19,8 +19,8 @@
</v-btn> </v-btn>
</v-card-title> </v-card-title>
<v-card-text> <v-card-text>
<div class="items-container pt-2 mb-n4"> <div class="items-container pt-2 mb-n4" :class="{'mx-n2' : isForm}">
<div class="text-right mb-2 mt-n4 mx-2"> <div class="text-right mb-2 mt-n2 mx-2">
<v-btn <v-btn
v-if="isForm" v-if="isForm"
x-small x-small
@ -63,20 +63,20 @@
</div> </div>
<v-card-title class="primary-value textColor--text text--lighten-2">{{ ch[primaryCol] }} <v-card-title class="primary-value textColor--text text--lighten-2">{{ ch[primaryCol] }}
<span class="grey--text caption primary-key" <span class="grey--text caption primary-key ml-1"
v-if="primaryKey">(Primary Key : {{ ch[primaryKey] }})</span> v-if="primaryKey"> (Primary Key : {{ ch[primaryKey] }})</span>
</v-card-title> </v-card-title>
</v-card> </v-card>
</template> </template>
<div v-else-if="data || localState" class="text-center textLight--text" <div v-else-if="data || localState" class="text-center textLight--text"
:class="{'pt-6 pb-4' : !isForm , 'pt-1':isForm}"> :class="{'pt-6 pb-4' : !isForm , 'pt-4 pb-3':isForm}">
No item{{ bt ? '' : 's' }} found No item{{ bt ? '' : 's' }} found
</div> </div>
<div v-if="isForm" class="mb-2 d-flex align-center justify-center"> <div v-if="isForm" class="mb-2 d-flex align-center justify-center">
<pagination <pagination
v-if="!bt && isDataAvail && data && data.count > 1" v-if="!bt && data && data.count > 1"
:size="size" :size="size"
:count="data && data.count" :count="data && data.count"
v-model="page" v-model="page"
@ -87,7 +87,7 @@
</v-card-text> </v-card-text>
<v-card-actions v-if="!isForm" class="justify-center flex-column" :class="{'py-0':isForm}"> <v-card-actions v-if="!isForm" class="justify-center flex-column" :class="{'py-0':isForm}">
<pagination <pagination
v-if="!bt && isDataAvail && data && data.count > 1" v-if="!bt && data && data.count > 1"
:size="size" :size="size"
:count="data && data.count" :count="data && data.count"
v-model="page" v-model="page"
@ -186,6 +186,12 @@ export default {
} }
.items-container {
overflow-x: visible;
max-height: min(500px, 60vh);
overflow-y: auto;
}
</style> </style>
<!-- <!--

27
packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listChildItemsModal.vue

@ -1,5 +1,6 @@
<template> <template>
<v-dialog v-model="show" width="600"> <v-dialog v-model="show" width="600" content-class="dialog">
<v-icon small class="close-icon" @click="$emit('input',false)">mdi-close</v-icon>
<list-child-items <list-child-items
v-if="show" v-if="show"
ref="child" ref="child"
@ -75,22 +76,18 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
::v-deep {
.dialog {
position: relative;
.child-list-modal { .close-icon {
position: relative; width: auto;
position: absolute;
.remove-child-icon { right: 10px;
position: absolute; top: 10px;
right: 10px; z-index: 9;
top: 10px; }
bottom: 10px;
opacity: 0;
}
&:hover .remove-child-icon {
opacity: 1;
} }
} }
</style> </style>

16
packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listItems.vue

@ -1,5 +1,6 @@
<template> <template>
<v-dialog v-model="show" width="600"> <v-dialog v-model="show" width="600" content-class="dialog">
<v-icon small class="close-icon" @click="$emit('input',false)">mdi-close</v-icon>
<v-card width="600" > <v-card width="600" >
<v-card-title class="textColor--text mx-2 justify-center">{{ title }} <v-card-title class="textColor--text mx-2 justify-center">{{ title }}
@ -184,6 +185,19 @@ export default {
overflow-y: auto; overflow-y: auto;
} }
::v-deep {
.dialog {
position: relative;
.close-icon {
width: auto;
position: absolute;
right: 10px;
top: 10px;
z-index: 9;
}
}
}
</style> </style>
<!-- <!--
/** /**

2
packages/nc-gui/components/project/spreadsheet/mixins/spreadsheet.js

@ -8,7 +8,7 @@ export default {
sortList: [], sortList: [],
showFields: {}, showFields: {},
// fieldList: [], // fieldList: [],
meta: {}, // meta: {},
data: [], data: [],
}), }),
methods: { methods: {

21
packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue

@ -869,13 +869,19 @@ export default {
}, },
async loadMeta(updateShowFields = true) { async loadMeta(updateShowFields = true) {
this.loadingMeta = true; this.loadingMeta = true;
const tableMeta = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ // const tableMeta = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias
// }, 'tableXcModelGet', {
// tn: this.table
// }]);
// this.meta = JSON.parse(tableMeta.meta);
const tableMeta = await this.$store.dispatch('meta/ActLoadMeta', {
env: this.nodes.env, env: this.nodes.env,
dbAlias: this.nodes.dbAlias dbAlias: this.nodes.dbAlias,
}, 'tableXcModelGet', { tn: this.table,
tn: this.table force: true
}]); });
this.meta = JSON.parse(tableMeta.meta);
this.loadingMeta = false; this.loadingMeta = false;
if (updateShowFields) { if (updateShowFields) {
try { try {
@ -933,6 +939,9 @@ export default {
} }
}, },
computed: { computed: {
meta() {
return this.$store.state.meta.metas[this.table];
},
currentApiUrl() { currentApiUrl() {
return this.api && `${this.api.apiUrl}?` + Object.entries(this.queryParams).filter(p => p[1]).map(([key, val]) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`).join('&') return this.api && `${this.api.apiUrl}?` + Object.entries(this.queryParams).filter(p => p[1]).map(([key, val]) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`).join('&')
}, },

2
packages/nc-gui/components/sponsorMini.vue

@ -1,5 +1,5 @@
<template> <template>
<v-card max-width="300" min-height="" class=" pb-3" href="https://github.com/sponsors/xgenecloud" <v-card max-width="300" min-height="" class=" pb-3" href="https://github.com/sponsors/nocodb"
target="_blank"> target="_blank">

2
packages/nc-gui/components/sponsorOverlay.vue

@ -1,6 +1,6 @@
<template> <template>
<v-overlay opacity=".98"> <v-overlay opacity=".98">
<v-card light width="450" min-height="500" class="" href="https://github.com/sponsors/xgenecloud" <v-card light width="450" min-height="500" class="" href="https://github.com/sponsors/nocodb"
target="_blank"> target="_blank">

10
packages/nc-gui/store/meta.js

@ -16,8 +16,8 @@ export const mutations = {
}; };
export const actions = { export const actions = {
async ActLoadMeta({state, commit, dispatch}, {tn, env, dbAlias}) { async ActLoadMeta({state, commit, dispatch}, {tn, env, dbAlias, force}) {
if (state.loading[tn]) { if (!force && state.loading[tn]) {
return await new Promise(resolve => { return await new Promise(resolve => {
const unsubscribe = this.app.store.subscribe(s => { const unsubscribe = this.app.store.subscribe(s => {
if (s.type === 'meta/MutLoading' && s.payload.key === tn && !s.payload.value) { if (s.type === 'meta/MutLoading' && s.payload.key === tn && !s.payload.value) {
@ -27,7 +27,7 @@ export const actions = {
}) })
}) })
} }
if (state.metas[tn]) { if (!force && state.metas[tn]) {
return state.metas[tn]; return state.metas[tn];
} }
commit('MutLoading', { commit('MutLoading', {
@ -35,14 +35,16 @@ export const actions = {
value: true value: true
}) })
const model = await dispatch('sqlMgr/ActSqlOp', [{env, dbAlias}, 'tableXcModelGet', {tn}], {root: true}); const model = await dispatch('sqlMgr/ActSqlOp', [{env, dbAlias}, 'tableXcModelGet', {tn}], {root: true});
const meta = JSON.parse(model.meta);
commit('MutMeta', { commit('MutMeta', {
key: tn, key: tn,
value: JSON.parse(model.meta) value: meta
}) })
commit('MutLoading', { commit('MutLoading', {
key: tn, key: tn,
value: undefined value: undefined
}) })
return force ? model : meta;
} }
} }

6
packages/nc-gui/store/sqlMgr.js

@ -388,7 +388,7 @@ export const actions = {
})).data; })).data;
// clear meta cache on relation create/delete /* // clear meta cache on relation create/delete
// todo: clear only necessary metas // todo: clear only necessary metas
// todo: include missing operations // todo: include missing operations
if ([ if ([
@ -406,7 +406,7 @@ export const actions = {
if (op === 'tableXcModelGet') { if (op === 'tableXcModelGet') {
try { try {
const meta = JSON.parse(model.meta); const meta = JSON.parse(data.meta);
commit('meta/MutMeta', { commit('meta/MutMeta', {
key: meta.tn, key: meta.tn,
value: meta value: meta
@ -414,7 +414,7 @@ export const actions = {
} catch (e) { } catch (e) {
// ignore // ignore
} }
} }*/
return data; return data;
} catch (e) { } catch (e) {

2
packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts

@ -1277,7 +1277,7 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
meta.v = [ meta.v = [
...meta.v.filter(vc => !(vc.hm && meta.manyToMany.some(mm => vc.hm.tn === mm.vtn))), ...meta.v.filter(vc => !(vc.hm && meta.manyToMany.some(mm => vc.hm.tn === mm.vtn))),
// todo: ignore existing m2m relations // todo: ignore duplicate m2m relations
...meta.manyToMany.map(mm => { ...meta.manyToMany.map(mm => {
if (queryParams?.showFields && !(`${mm._tn} <=> ${mm._rtn}` in queryParams.showFields)) { if (queryParams?.showFields && !(`${mm._tn} <=> ${mm._rtn}` in queryParams.showFields)) {

12
packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts

@ -1916,6 +1916,18 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
// todo : add loaders // todo : add loaders
if (meta.manyToMany) {
for (const mm of meta.manyToMany) {
await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', {
title: `${mm.tn}Mm${mm.rtn}List`,
parent: mm.tn,
child: mm.rtn,
relation: 'mm',
resolver: 'mmlist',
});
}
}
} }
} }

Loading…
Cancel
Save