Browse Source

feat: M2M corrections

Signed-off-by: Pranav C <61551451+pranavxc@users.noreply.github.com>
pull/341/head
Pranav C 3 years ago
parent
commit
be2bc9093d
  1. 2
      packages/nc-gui/components/ProjectTreeViewOld.vue
  2. 2
      packages/nc-gui/components/createOrEditProject.vue
  3. 70
      packages/nc-gui/components/project/appStore/inputs/booleanCell.vue
  4. 76
      packages/nc-gui/components/project/appStore/inputs/datePickerCell.vue
  5. 92
      packages/nc-gui/components/project/appStore/inputs/dateTimePickerCell.vue
  6. 89
      packages/nc-gui/components/project/appStore/inputs/enumListEditableCell.vue
  7. 101
      packages/nc-gui/components/project/appStore/inputs/enumRadioEditableCell.vue
  8. 69
      packages/nc-gui/components/project/appStore/inputs/floatCell.vue
  9. 69
      packages/nc-gui/components/project/appStore/inputs/integerCell.vue
  10. 97
      packages/nc-gui/components/project/appStore/inputs/jsonCell.vue
  11. 100
      packages/nc-gui/components/project/appStore/inputs/setListCheckboxCell.vue
  12. 91
      packages/nc-gui/components/project/appStore/inputs/setListEditableCell.vue
  13. 78
      packages/nc-gui/components/project/appStore/inputs/timePickerCell.vue
  14. 33
      packages/nc-gui/components/project/spreadsheet/components/cell/setListCell.vue
  15. 6
      packages/nc-gui/components/project/spreadsheet/components/editColumn.vue
  16. 23
      packages/nc-gui/components/project/spreadsheet/components/editColumn/linkedToAnotherOptions.vue
  17. 29
      packages/nc-gui/components/project/spreadsheet/components/editColumn/relationOptions.vue
  18. 18
      packages/nc-gui/components/project/spreadsheet/components/editVirtualColumn.vue
  19. 6
      packages/nc-gui/components/project/spreadsheet/components/editableCell/datePickerCell.vue
  20. 18
      packages/nc-gui/components/project/spreadsheet/components/editableCell/dateTimePickerCell.vue
  21. 6
      packages/nc-gui/components/project/spreadsheet/components/editableCell/enumListEditableCell.vue
  22. 2
      packages/nc-gui/components/project/spreadsheet/components/editableCell/floatCell.vue
  23. 5
      packages/nc-gui/components/project/spreadsheet/components/editableCell/setListEditableCell.vue
  24. 7
      packages/nc-gui/components/project/spreadsheet/components/editableCell/textAreaCell.vue
  25. 36
      packages/nc-gui/components/project/spreadsheet/components/expandedForm.vue
  26. 1
      packages/nc-gui/components/project/spreadsheet/components/headerCell.vue
  27. 9
      packages/nc-gui/components/project/spreadsheet/components/virtualCell.vue
  28. 9
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/belogsToCell.vue
  29. 2
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listChildItems.vue
  30. 16
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listItems.vue
  31. 10
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue
  32. 7
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue
  33. 4
      packages/nc-gui/components/project/spreadsheet/components/virtualHeaderCell.vue
  34. 21
      packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue
  35. 14
      packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue
  36. 26
      packages/nc-gui/components/project/tableTabs/columns.vue
  37. 4
      packages/nc-gui/components/project/xcInfo.vue
  38. 2
      packages/nc-gui/components/utils/dlgProjectCreate.vue
  39. 12
      packages/nc-gui/helpers/treeViewIcons.js
  40. 4
      packages/nc-gui/helpers/treeViewIconsColors.js
  41. 22
      packages/nc-gui/layouts/default.vue
  42. 14
      packages/nc-gui/nuxt.config.js
  43. 2
      packages/nc-gui/pages/project/id.vue
  44. 2
      packages/nc-gui/pages/project/name.vue
  45. 2
      packages/nc-gui/pages/project/xcdb.vue
  46. 2
      packages/nc-gui/pages/projects/index.vue
  47. 7
      packages/nc-gui/store/sqlMgr.js
  48. 42
      packages/nocodb/package-lock.json
  49. 3
      packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts
  50. 4
      packages/nocodb/src/lib/noco/NcProjectBuilder.ts
  51. 16
      packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts
  52. 3
      packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts
  53. 4
      packages/nocodb/src/lib/noco/gql/GqlResolver.ts
  54. 256
      packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts
  55. 2
      packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMysql.ts

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

@ -153,7 +153,7 @@
<v-tooltip bottom> <v-tooltip bottom>
<template v-slot:activator="{on}"> <template v-slot:activator="{on}">
<div class="d-100 d-flex" v-on="on"> <div class="d-100 d-flex" v-on="on">
<v-icon color="orange" class="mr-1" small>mdi-settings</v-icon> <v-icon color="orange" class="mr-1" small>mdi-cog</v-icon>
<span>Settings</span> <span>Settings</span>
</div> </div>
</template> </template>

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

@ -1043,7 +1043,7 @@ export default {
{ text: 'Disabled', value: 'none' }, { text: 'Disabled', value: 'none' },
], ],
projectTypes: [ projectTypes: [
{ text: 'REST APIs', value: 'rest', icon: 'mdi-json', iconColor: 'green' }, { text: 'REST APIs', value: 'rest', icon: 'mdi-code-json', iconColor: 'green' },
{ text: 'GRAPHQL APIs', value: 'graphql', icon: 'mdi-graphql', iconColor: 'pink' }, { text: 'GRAPHQL APIs', value: 'graphql', icon: 'mdi-graphql', iconColor: 'pink' },
// { // {
// text: 'Automatic gRPC APIs on database', // text: 'Automatic gRPC APIs on database',

70
packages/nc-gui/components/project/appStore/inputs/booleanCell.vue

@ -1,70 +0,0 @@
<template>
<div class="d-flex align-center " :class="{'justify-center':!isForm}">
<input v-on="parentListeners" type="checkbox" v-model="localState">
</div>
</template>
<script>
export default {
name: "boolean-cell",
props: {
value: [String,Number, Boolean],
isForm: Boolean
},
mounted() {
this.$el.focus();
},
computed: {
localState: {
get() {
return this.value
},
set(val) {
this.$emit('input', val);
// this.$emit('update');
}
},
parentListeners() {
const $listeners = {};
if (this.$listeners.blur) {
$listeners.blur = this.$listeners.blur;
}
if (this.$listeners.focus) {
$listeners.focus = this.$listeners.focus;
}
return $listeners;
},
}
}
</script>
<style scoped>
</style>
<!--
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-->

76
packages/nc-gui/components/project/appStore/inputs/datePickerCell.vue

@ -1,76 +0,0 @@
<template>
<v-menu>
<template v-slot:activator="{on}">
<div class="value" v-on="on">{{ localState }}</div>
</template>
<v-date-picker v-on="parentListeners" v-model="localState"></v-date-picker>
</v-menu>
</template>
<script>
export default {
name: "date-picker-cell", props: {
value: [String, Date]
},
mounted() {
if (this.$el && this.$el.$el) {
this.$el.$el.focus();
}
},
computed: {
localState: {
get() {
return typeof this.value === 'string' ? this.value.replace(/(\d)T(?=\d)/, '$1 ') : this.value;
},
set(val) {
this.$emit('input', new Date(val).toJSON().slice(0, 10));
}
},
parentListeners() {
const $listeners = {};
if (this.$listeners.blur) {
$listeners.blur = this.$listeners.blur;
}
if (this.$listeners.focus) {
$listeners.focus = this.$listeners.focus;
}
return $listeners;
},
}
}
</script>
<style scoped>
.value {
width: 100%;
height: 100%;
min-height:20px;
}
</style>
<!--
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-->

92
packages/nc-gui/components/project/appStore/inputs/dateTimePickerCell.vue

@ -1,92 +0,0 @@
<template>
<v-datetime-picker
v-on="parentListeners"
class="caption xc-date-time-picker"
ref="picker"
:text-field-props="{
class:'caption mt-n1 pt-0'
}"
:time-picker-props="{
format:'24hr'
}"
v-model="localState"
></v-datetime-picker>
</template>
<script>
export default {
name: "date-time-picker-cell",
props: ['value', 'ignoreFocus'],
mounted() {
if (!this.ignoreFocus) {
this.$refs.picker.display = true;
}
},
computed: {
localState: {
get() {
if(/^\d{6,}$/.test(this.value)){
return new Date(+this.value);
}
return /\dT\d/.test(this.value) ? new Date(this.value.replace(/(\d)T(?=\d)/, '$1 ')) : this.value;
},
set(val) {
// if(/^\d{6,}$/.test(this.value)){
// return this.$emit('input', new Date(this.value).getTime());
// }
const uVal = new Date(val).toISOString().slice(0, 19).replace('T', ' ').replace(/(\d{1,2}:\d{1,2}):\d{1,2}$/,'$1');
console.log(val, uVal)
this.$emit('input', uVal);
}
},
parentListeners(){
const $listeners = {};
if(this.$listeners.blur){
$listeners.blur = this.$listeners.blur;
}
if(this.$listeners.focus){
$listeners.focus = this.$listeners.focus;
}
return $listeners;
},
}
}
</script>
<style scoped>
/deep/ .v-input, /deep/ .v-text-field {
margin-top: 0 !important;
padding-top: 0 !important;
font-size: inherit !important;
}
</style>
<!--
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-->

89
packages/nc-gui/components/project/appStore/inputs/enumListEditableCell.vue

@ -1,89 +0,0 @@
<template>
<select v-on="parentListeners" v-model="localState">
<option v-for="eVal of enumValues" :key="eVal" :value="eVal">{{ eVal }}</option>
</select>
</template>
<script>
export default {
name: "enum-list-cell",
props: {
value: String,
column: Object
},
mounted() {
this.$el.focus();
let event;
event = document.createEvent('MouseEvents');
event.initMouseEvent('mousedown', true, true, window);
this.$el.dispatchEvent(event);
},
computed: {
localState: {
get() {
return this.value
},
set(val) {
this.$emit('input', val);
this.$emit('update');
}
},
enumValues() {
if (this.column && this.column.dtxp) {
return this.column.dtxp.split(',').map(v => v.replace(/^'|'$/g, ''))
}
return [];
},
parentListeners(){
const $listeners = {};
if(this.$listeners.blur){
$listeners.blur = this.$listeners.blur;
}
if(this.$listeners.focus){
$listeners.focus = this.$listeners.focus;
}
return $listeners;
},
}
}
</script>
<style scoped>
select {
width: 100%;
height: 100%;
color: var(--v-textColor-base);
-webkit-appearance: menulist;
/*webkit browsers */
-moz-appearance: menulist;
/*Firefox */
appearance: menulist;
}
</style>
<!--
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-->

101
packages/nc-gui/components/project/appStore/inputs/enumRadioEditableCell.vue

@ -1,101 +0,0 @@
<template>
<div class="d-flex align-center">
<div>
<div class="item" v-for="(val,i) of enumValues" :key="val">
<input type="radio" :id="`key-radio-${val}`" class="orange--text" v-model="localState" :value="val">
<label class="py-1 px-3 d-inline-block my-1 label" :for="`key-radio-${val}`"
:style="{
background:colors[i % colors.length ]
}"
>{{ val }}</label>
</div>
</div>
</div>
</template>
<script>
import {enumColor as colors} from "@/components/project/spreadsheet/helpers/colors";
export default {
name: "enum-radio-editable-cell",
props: {
value: String,
column: Object
},
mounted() {
// this.$el.focus();
// let event;
// event = document.createEvent('MouseEvents');
// event.initMouseEvent('mousedown', true, true, window);
// this.$el.dispatchEvent(event);
},
computed: {
colors() {
return this.$store.state.windows.darkTheme ? colors.dark : colors.light;
},
localState: {
get() {
return this.value
},
set(val) {
this.$emit('input', val);
this.$emit('update');
}
},
enumValues() {
if (this.column && this.column.dtxp) {
return this.column.dtxp.split(',').map(v => v.replace(/^'|'$/g, ''))
}
return [];
},
parentListeners() {
const $listeners = {};
if (this.$listeners.blur) {
$listeners.blur = this.$listeners.blur;
}
if (this.$listeners.focus) {
$listeners.focus = this.$listeners.focus;
}
return $listeners;
},
}
}
</script>
<style scoped>
.label {
border-radius: 25px;
}
.item {
white-space: nowrap;
}
</style>
<!--
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-->

69
packages/nc-gui/components/project/appStore/inputs/floatCell.vue

@ -1,69 +0,0 @@
<template>
<input v-on="parentListeners" v-model="localState" type="number">
</template>
<script>
export default {
name: "floatCell",
props: {
value: [String,Number]
},
mounted() {
this.$el.focus();
},
computed: {
localState: {
get() {
return this.value
},
set(val) {
this.$emit('input', val);
}
},
parentListeners(){
const $listeners = {};
if(this.$listeners.blur){
$listeners.blur = this.$listeners.blur;
}
if(this.$listeners.focus){
$listeners.focus = this.$listeners.focus;
}
return $listeners;
},
}
}
</script>
<style scoped>
input {
width: 100%;
height: 100%;
color: var(--v-textColor-base);
}
</style>
<!--
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-->

69
packages/nc-gui/components/project/appStore/inputs/integerCell.vue

@ -1,69 +0,0 @@
<template>
<input v-on="parentListeners" v-model="localState" type="number">
</template>
<script>
export default {
name: "integerCell",
props: {
value: [String, Number]
},
mounted() {
this.$el.focus();
},
computed: {
localState: {
get() {
return this.value
},
set(val) {
this.$emit('input', parseInt(val, 10));
}
},
parentListeners(){
const $listeners = {};
if(this.$listeners.blur){
$listeners.blur = this.$listeners.blur;
}
if(this.$listeners.focus){
$listeners.focus = this.$listeners.focus;
}
return $listeners;
},
}
}
</script>
<style scoped>
input {
width: 100%;
height: 100%;
color: var(--v-textColor-base);
}
</style>
<!--
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-->

97
packages/nc-gui/components/project/appStore/inputs/jsonCell.vue

@ -1,97 +0,0 @@
<template>
<div @keydown.stop.enter class="cell-container">
<div class="d-flex ma-1" v-if="!isForm">
<v-spacer>
</v-spacer>
<v-btn outlined x-small class="mr-1" @click="$emit('cancel')">Cancel</v-btn>
<v-btn x-small color="primary" @click="save">Save</v-btn>
</div>
<monaco-json-object-editor v-model="localState"
style="width: 300px;min-height: 200px;min-width:100%"></monaco-json-object-editor>
</div>
</template>
<script>
import MonacoJsonEditor from "@/components/monaco/MonacoJsonEditor";
import MonacoJsonObjectEditor from "@/components/monaco/MonacoJsonObjectEditor";
export default {
name: "json-cell",
components: {MonacoJsonObjectEditor, MonacoJsonEditor},
props: {
value: String,
isForm:Boolean
},
data: () => ({
localState: ''
}),
created() {
this.localState = typeof this.value === 'string' ? JSON.parse(this.value) : this.value;
},
mounted() {
}, watch: {
value(val) {
this.localState = typeof val === 'string' ? JSON.parse(val) : val;
},
localState(val){
if(this.isForm){
this.$emit('input', JSON.stringify(val))
}
}
},
methods: {
save() {
this.$emit('input', JSON.stringify(this.localState))
}
},
computed:{
parentListeners(){
const $listeners = {};
if(this.$listeners.blur){
$listeners.blur = this.$listeners.blur;
}
if(this.$listeners.focus){
$listeners.focus = this.$listeners.focus;
}
return $listeners;
},
}
}
</script>
<style scoped>
.cell-container {
/*margin: 0 -5px;*/
/*position: relative;*/
width: 100%
}
</style>
<!--
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-->

100
packages/nc-gui/components/project/appStore/inputs/setListCheckboxCell.vue

@ -1,100 +0,0 @@
<template>
<div class="d-flex align-center">
<div>
<div class="" v-for="(val,i) of setValues" :key="val">
<input type="checkbox" :id="`key-check-box-${val}`" class="orange--text" v-model="localState" :value="val">
<label class="py-1 px-3 d-inline-block my-1 label" :for="`key-check-box-${val}`"
:style="{
background:colors[i % colors.length ]
}"
>{{ val }}</label>
</div>
</div>
</div>
</template>
<script>
import colors from "@/components/project/spreadsheet/helpers/colors";
export default {
name: "set-list-checkbox-cell",
props: {
value: String,
column: Object
},
data() {
},
mounted() {
this.$el.focus();
let event;
event = document.createEvent('MouseEvents');
event.initMouseEvent('mousedown', true, true, window);
this.$el.dispatchEvent(event);
},
computed: {
colors() {
return this.$store.state.windows.darkTheme ? colors.dark : colors.light;
},
localState: {
get() {
return this.value && this.value.split(',')
},
set(val) {
this.$emit('input', val.join(','));
this.$emit('update');
}
},
setValues() {
if (this.column && this.column.dtxp) {
return this.column.dtxp.split(',').map(v => v.replace(/^'|'$/g, ''))
}
return [];
},
parentListeners() {
const $listeners = {};
if (this.$listeners.blur) {
$listeners.blur = this.$listeners.blur;
}
if (this.$listeners.focus) {
$listeners.focus = this.$listeners.focus;
}
return $listeners;
},
}
}
</script>
<style scoped>
.label {
border-radius: 25px;
}
</style>
<!--
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-->

91
packages/nc-gui/components/project/appStore/inputs/setListEditableCell.vue

@ -1,91 +0,0 @@
<template>
<div>
<select v-on="parentListeners" v-model="localState" multiple>
<option v-for="val of setValues" :key="val" :value="val">{{ val }}</option>
</select>
</div>
</template>
<script>
export default {
name: "set-list-cell",
props: {
value: String,
column: Object
},
mounted() {
this.$el.focus();
let event;
event = document.createEvent('MouseEvents');
event.initMouseEvent('mousedown', true, true, window);
this.$el.dispatchEvent(event);
},
computed: {
localState: {
get() {
return this.value && this.value.split(',')
},
set(val) {
this.$emit('input', val.join(','));
this.$emit('update');
}
},
setValues() {
if (this.column && this.column.dtxp) {
return this.column.dtxp.split(',').map(v => v.replace(/^'|'$/g, ''))
}
return [];
},
parentListeners(){
const $listeners = {};
if(this.$listeners.blur){
$listeners.blur = this.$listeners.blur;
}
if(this.$listeners.focus){
$listeners.focus = this.$listeners.focus;
}
return $listeners;
},
}
}
</script>
<style scoped>
select {
width: 100%;
height: 100%;
color: var(--v-textColor-base);
-webkit-appearance: menulist;
/*webkit browsers */
-moz-appearance: menulist;
/*Firefox */
appearance: menulist;
}
</style>
<!--
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-->

78
packages/nc-gui/components/project/appStore/inputs/timePickerCell.vue

@ -1,78 +0,0 @@
<template>
<v-menu>
<template v-slot:activator="{on}">
<div class="value" v-on="on">{{ localState }}</div>
</template>
<v-time-picker v-on="parentListeners" v-model="localState"></v-time-picker>
</v-menu>
</template>
<script>
export default {
name: "time-picker-cell",
props: {
value: [String, Date]
},
mounted() {
if (this.$el && this.$el.$el) {
this.$el.$el.focus();
}
},
computed: {
localState: {
get() {
return typeof this.value === 'string' ? this.value.replace(/(\d)T(?=\d)/, '$1 ') : this.value;
},
set(val) {
this.$emit('input', (new Date(val).toJSON() || '').slice(0, 10));
}
},
parentListeners() {
const $listeners = {};
if (this.$listeners.blur) {
$listeners.blur = this.$listeners.blur;
}
if (this.$listeners.focus) {
$listeners.focus = this.$listeners.focus;
}
if (this.$listeners.cancel) {
$listeners.cancel = this.$listeners.cancel;
}
return $listeners;
},
}
}
</script>
<style scoped>
.value {
min-height: 20px;
}
</style>
<!--
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-->

33
packages/nc-gui/components/project/spreadsheet/components/cell/setListCell.vue

@ -1,38 +1,43 @@
<template> <template>
<div> <div>
<span v-for="v in (value || '').split(',')" :key="v" :style="{ <v-chip
background:colors[v] small
}" class="set-item ma-1 py-1 px-3">{{ v }}</span> v-for="v in (value || '').split(',')"
:key="v"
:color="colors[setValues.indexOf(v) % colors.length]"
class="set-item ma-1 py-1 px-3"
>
{{ v }}
</v-chip>
</div> </div>
</template> </template>
<script> <script>
import colors from "@/components/project/spreadsheet/helpers/colors"; import colors from "@/mixins/colors";
export default { export default {
props: ['value', 'column'], props: ['value', 'column'],
name: "setListCell", name: "setListCell",
mixins: [colors],
computed: { computed: {
colors() {
const col = this.$store.state.windows.darkTheme ? colors.dark : colors.light; setValues() {
if (this.column && this.column.dtxp) { if (this.column && this.column.dtxp) {
return this.column.dtxp.split(',').map(v => v.replace(/^'|'$/g, '')).reduce((obj, v, i) => ({ return this.column.dtxp.split(',').map(v => v.replace(/^'|'$/g, ''))
...obj,
[v]: col[i]
}), {})
} }
return {}; return [];
} },
} }
} }
</script> </script>
<style scoped> <style scoped>
/*
.set-item { .set-item {
display: inline-block; display: inline-block;
border-radius: 25px; border-radius: 25px;
white-space: nowrap; white-space: nowrap;
} }*/
</style> </style>
<!-- <!--
/** /**

6
packages/nc-gui/components/project/spreadsheet/components/editColumn.vue

@ -95,6 +95,7 @@
:meta="meta" :meta="meta"
:isSQLite="isSQLite" :isSQLite="isSQLite"
:alias="newColumn.cn" :alias="newColumn.cn"
:isMSSQL="isMSSQL"
@onColumnSelect="onRelColumnSelect" @onColumnSelect="onRelColumnSelect"
></linked-to-another-options> ></linked-to-another-options>
</v-col> </v-col>
@ -104,6 +105,7 @@
ref="relation" ref="relation"
:column="newColumn" :column="newColumn"
:nodes="nodes" :nodes="nodes"
:isMSSQL="isMSSQL"
@onColumnSelect="onRelColumnSelect" @onColumnSelect="onRelColumnSelect"
:isSQLite="isSQLite" :isSQLite="isSQLite"
></relation-options> ></relation-options>
@ -312,6 +314,7 @@ import RelationOptions from "@/components/project/spreadsheet/components/editCol
import DlgLabelSubmitCancel from "@/components/utils/dlgLabelSubmitCancel"; import DlgLabelSubmitCancel from "@/components/utils/dlgLabelSubmitCancel";
import LinkedToAnotherOptions from "@/components/project/spreadsheet/components/editColumn/linkedToAnotherOptions"; import LinkedToAnotherOptions from "@/components/project/spreadsheet/components/editColumn/linkedToAnotherOptions";
import {SqliteUi} from "@/helpers/SqliteUi"; import {SqliteUi} from "@/helpers/SqliteUi";
import {MssqlUi} from "@/helpers/MssqlUi";
export default { export default {
name: "editColumn", name: "editColumn",
@ -515,6 +518,9 @@ export default {
isSQLite() { isSQLite() {
return this.sqlUi === SqliteUi; return this.sqlUi === SqliteUi;
}, },
isMSSQL() {
return this.sqlUi === MssqlUi;
},
dataTypes() { dataTypes() {
return this.sqlUi.getDataTypeListForUiType(this.newColumn) return this.sqlUi.getDataTypeListForUiType(this.newColumn)
}, },

23
packages/nc-gui/components/project/spreadsheet/components/editColumn/linkedToAnotherOptions.vue

@ -113,13 +113,6 @@ export default {
relation: {}, relation: {},
isRefTablesLoading: false, isRefTablesLoading: false,
isRefColumnsLoading: false, isRefColumnsLoading: false,
onUpdateDeleteOptions: [
"NO ACTION",
"CASCADE",
"RESTRICT",
"SET NULL",
"SET DEFAULT"
],
}), }),
async created() { async created() {
await this.loadTablesList(); await this.loadTablesList();
@ -128,13 +121,25 @@ export default {
childTable: this.nodes.tn, childTable: this.nodes.tn,
parentTable: this.column.rtn || "", parentTable: this.column.rtn || "",
parentColumn: this.column.rcn || "", parentColumn: this.column.rcn || "",
onDelete: "CASCADE", onDelete: "NO ACTION",
onUpdate: "CASCADE", onUpdate: "NO ACTION",
updateRelation: this.column.rtn ? true : false, updateRelation: this.column.rtn ? true : false,
type: 'real' type: 'real'
} }
}, },
computed: { computed: {
onUpdateDeleteOptions() {
if (this.isMSSQL) {
return ["NO ACTION"]
}
return [
"NO ACTION",
"CASCADE",
"RESTRICT",
"SET NULL",
"SET DEFAULT"
];
},
tableRules() { tableRules() {
return [ return [
v => !!v || 'Required', v => !!v || 'Required',

29
packages/nc-gui/components/project/spreadsheet/components/editColumn/relationOptions.vue

@ -97,20 +97,13 @@
<script> <script>
export default { export default {
name: "relationOptions", name: "relationOptions",
props: ['nodes', 'column', 'isSQLite'], props: ['nodes', 'column', 'isSQLite', 'isMSSQL'],
data: () => ({ data: () => ({
refTables: [], refTables: [],
refColumns: [], refColumns: [],
relation: {}, relation: {},
isRefTablesLoading: false, isRefTablesLoading: false,
isRefColumnsLoading: false, isRefColumnsLoading: false,
onUpdateDeleteOptions: [
"NO ACTION",
"CASCADE",
"RESTRICT",
"SET NULL",
"SET DEFAULT"
],
}), }),
async created() { async created() {
await this.loadTablesList(); await this.loadTablesList();
@ -119,12 +112,28 @@ export default {
childTable: this.nodes.tn, childTable: this.nodes.tn,
parentTable: this.column.rtn || "", parentTable: this.column.rtn || "",
parentColumn: this.column.rcn || "", parentColumn: this.column.rcn || "",
onDelete: "CASCADE", onDelete: "NO ACTION",
onUpdate: "CASCADE", onUpdate: "NO ACTION",
updateRelation: this.column.rtn ? true : false, updateRelation: this.column.rtn ? true : false,
type: this.isSQLite ? 'virtual' : 'real' type: this.isSQLite ? 'virtual' : 'real'
} }
}, },
computed: {
onUpdateDeleteOptions() {
if (this.isMSSQL) {
return [
"NO ACTION", x]
}
return [
"NO ACTION",
"CASCADE",
"RESTRICT",
"SET NULL",
"SET DEFAULT"
];
}
},
methods: { methods: {
async loadColumnList() { async loadColumnList() {
if (!this.relation.parentTable) return; if (!this.relation.parentTable) return;

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

@ -8,7 +8,7 @@
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-btn x-small outlined @click="close">Cancel</v-btn> <v-btn x-small outlined @click="close">Cancel</v-btn>
<v-btn x-small color="primary" @click="comingSoon" :disabled="!valid">Save</v-btn> <v-btn x-small color="primary" @click="save" :disabled="!valid">Save</v-btn>
</v-col> </v-col>
<v-col cols="12"> <v-col cols="12">
<v-text-field <v-text-field
@ -57,12 +57,22 @@ export default {
}, },
async save() { async save() {
try { try {
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'xcUpdateVirtualKeyAlias', {
tn: this.nodes.tn,
oldAlias: this.column._cn,
newAlias: this.newColumn._cn,
}]);
this.$toast.success('Successfully updated alias').goAway(3000);
} catch (e) { } catch (e) {
console.log(e) console.log(e)
this.$toast.error('Failed to update column alias').goAway(3000);
} }
this.$emit('saved');
this.$emit('close'); this.$emit('input', false);
}, },
focusInput() { focusInput() {

6
packages/nc-gui/components/project/spreadsheet/components/editableCell/datePickerCell.vue

@ -23,10 +23,10 @@ export default {
computed: { computed: {
localState: { localState: {
get() { get() {
return typeof this.value === 'string' ? this.value.replace(/(\d)T(?=\d)/, '$1 ') : this.value; return typeof this.value === 'string' ? this.value.replace(/(\d)T(?=\d)/, '$1 ') : (this.value && new Date(this.value));
}, },
set(val) { set(val) {
this.$emit('input', new Date(val).toJSON().slice(0, 10)); this.$emit('input', val && new Date(val).toJSON().slice(0, 10));
} }
}, },
parentListeners() { parentListeners() {
@ -48,7 +48,7 @@ export default {
<style scoped> <style scoped>
.value { .value {
width: 100%; width: 100%;
min-height:20px; min-height: 20px;
} }
</style> </style>

18
packages/nc-gui/components/project/spreadsheet/components/editableCell/dateTimePickerCell.vue

@ -32,31 +32,25 @@ export default {
get() { get() {
// todo : time value correction // todo : time value correction
if(/^\d{6,}$/.test(this.value)){ if (/^\d{6,}$/.test(this.value)) {
return new Date(+this.value); return new Date(+this.value);
} }
return /\dT\d/.test(this.value) ? new Date(this.value.replace(/(\d)T(?=\d)/, '$1 ')) : this.value; return /\dT\d/.test(this.value) ? new Date(this.value.replace(/(\d)T(?=\d)/, '$1 ')) : (this.value && new Date(this.value));
}, },
set(val) { set(val) {
// if(/^\d{6,}$/.test(this.value)){ const uVal = val && new Date(val).toISOString().slice(0, 19).replace('T', ' ').replace(/(\d{1,2}:\d{1,2}):\d{1,2}$/, '$1');
// return this.$emit('input', new Date(this.value).getTime());
// }
const uVal = new Date(val).toISOString().slice(0, 19).replace('T', ' ').replace(/(\d{1,2}:\d{1,2}):\d{1,2}$/,'$1');
console.log(val, uVal)
this.$emit('input', uVal); this.$emit('input', uVal);
} }
}, },
parentListeners(){ parentListeners() {
const $listeners = {}; const $listeners = {};
if(this.$listeners.blur){ if (this.$listeners.blur) {
$listeners.blur = this.$listeners.blur; $listeners.blur = this.$listeners.blur;
} }
if(this.$listeners.focus){ if (this.$listeners.focus) {
$listeners.focus = this.$listeners.focus; $listeners.focus = this.$listeners.focus;
} }

6
packages/nc-gui/components/project/spreadsheet/components/editableCell/enumListEditableCell.vue

@ -1,11 +1,11 @@
<template> <template>
<v-select v-on="parentListeners" v-model="localState" dense flat :items="enumValues" hide-details class="mt-0" :clearable="!column.rqd"> <v-select solo v-on="parentListeners" v-model="localState" dense flat :items="enumValues" hide-details class="mt-0" :clearable="!column.rqd">
<!-- <option v-for="eVal of enumValues" :key="eVal" :value="eVal">{{ eVal }}</option>--> <!-- <option v-for="eVal of enumValues" :key="eVal" :value="eVal">{{ eVal }}</option>-->
<template v-slot:selection="{item}"> <template v-slot:selection="{item}">
<div class="d-100 pl-4" :class="{ <div class="d-100" :class="{
'text-center' : !isForm 'text-center' : !isForm
}"> }">
<v-chip small :color="colors[enumValues.indexOf(item) % colors.length]">{{ item }}</v-chip> <v-chip small :color="colors[enumValues.indexOf(item) % colors.length]" class="ma-1">{{ item }}</v-chip>
</div> </div>
</template> </template>
<template v-slot:item="{item}"> <template v-slot:item="{item}">

2
packages/nc-gui/components/project/spreadsheet/components/editableCell/floatCell.vue

@ -17,7 +17,7 @@ export default {
return this.value return this.value
}, },
set(val) { set(val) {
this.$emit('input', val); this.$emit('input', +val);
} }
}, },
parentListeners(){ parentListeners(){

5
packages/nc-gui/components/project/spreadsheet/components/editableCell/setListEditableCell.vue

@ -11,13 +11,14 @@
chips chips
flat flat
dense dense
solo
hide-details hide-details
deletable-chips deletable-chips
class="text-center mt-0" class="text-center mt-0 "
> >
<template v-slot:selection="data"> <template v-slot:selection="data">
<v-chip <v-chip
small small class="ma-1 "
:key="data" :key="data"
:color="colors[setValues.indexOf(data.item) % colors.length]" :color="colors[setValues.indexOf(data.item) % colors.length]"
@click:close="data.parent.selectItem(data.item)" @click:close="data.parent.selectItem(data.item)"

7
packages/nc-gui/components/project/spreadsheet/components/editableCell/textAreaCell.vue

@ -1,5 +1,8 @@
<template> <template>
<textarea v-on="parentListeners" ref="textarea" v-model="localState" rows="3" <textarea v-on="parentListeners"
rows="4"
ref="textarea"
v-model="localState"
@keydown.alt.enter.stop @keydown.alt.enter.stop
@keydown.shift.enter.stop @keydown.shift.enter.stop
></textarea> ></textarea>
@ -48,7 +51,7 @@ export default {
input, textarea { input, textarea {
width: 100%; width: 100%;
min-height: 60px; min-height: 60px;
height: calc(100% - 28px); height: 100%;
color: var(--v-textColor-base); color: var(--v-textColor-base);
} }
</style> </style>

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

@ -33,10 +33,15 @@
</div> </div>
</v-toolbar> </v-toolbar>
<div class="form-container "> <div class="form-container ">
<v-card-text class=" py-0 px-0 " :class="{ <v-card-text class=" py-0 px-0 " :class="{
'px-10' : isNew || !toggleDrawer, 'px-10' : isNew || !toggleDrawer,
}"> }">
<v-breadcrumbs class="caption pt-0 pb-2 justify-center d-100"
v-if="localBreadcrumbs && localBreadcrumbs.length"
:items="localBreadcrumbs.map(text => ({text}))"/>
<v-container fluid style="height:70vh" class="py-0"> <v-container fluid style="height:70vh" class="py-0">
<v-row class="h-100"> <v-row class="h-100">
@ -80,6 +85,7 @@
:sql-ui="sqlUi" :sql-ui="sqlUi"
:is-new="isNew" :is-new="isNew"
:is-form="true" :is-form="true"
:breadcrumbs="localBreadcrumbs"
@updateCol="updateCol" @updateCol="updateCol"
@newRecordsSaved="$listeners.loadTableData || (() => {})" @newRecordsSaved="$listeners.loadTableData || (() => {})"
></virtual-cell> ></virtual-cell>
@ -208,6 +214,12 @@ export default {
components: {VirtualHeaderCell, VirtualCell, EditableCell, HeaderCell}, components: {VirtualHeaderCell, VirtualCell, EditableCell, HeaderCell},
mixins: [colors], mixins: [colors],
props: { props: {
breadcrumbs: {
type: Array,
default() {
return [];
}
},
dbAlias: String, dbAlias: String,
value: Object, value: Object,
meta: Object, meta: Object,
@ -398,6 +410,9 @@ export default {
}, },
isChanged() { isChanged() {
return Object.values(this.changedColumns).some(Boolean) return Object.values(this.changedColumns).some(Boolean)
},
localBreadcrumbs() {
return [...this.breadcrumbs, `${this.table} (${this.localState && this.localState[this.primaryValueColumn]})`]
} }
} }
} }
@ -415,6 +430,17 @@ export default {
::v-deep { ::v-deep {
.v-breadcrumbs__item:nth-child(odd) {
font-size: .72rem;
color: grey;
}
.v-breadcrumbs li:nth-child(even) {
padding: 0 6px;
font-size: .72rem;
color: var(--v-textColor-base);
}
position: relative; position: relative;
.comment-icon { .comment-icon {
@ -423,10 +449,12 @@ export default {
bottom: 60px; bottom: 60px;
} }
/* 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 > select, & > div > select,
& > div > .xc-input > select, & > div > .xc-input > select,
& > div textarea { & > div textarea {
@ -456,9 +484,10 @@ export default {
background: #363636; background: #363636;
.row-col { .row-col {
& > div div > input, //& > div div > input,
& > div > input, & > div > input,
& > div > .xc-input > input, & > div > .xc-input > input,
& > div > .xc-input >div > input,
& > div > select, & > div > select,
& > div > .xc-input > select, & > div > .xc-input > select,
& > div textarea { & > div textarea {
@ -472,8 +501,9 @@ 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 > select, & > div > select,
& > div > .xc-input > select, & > div > .xc-input > select,
& > div textarea { & > div textarea {

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

@ -126,6 +126,7 @@ export default {
console.log(e) console.log(e)
} }
}, async setAsPrimaryValue() { }, async setAsPrimaryValue() {
// todo: pass only updated fields
try { try {
const meta = JSON.parse(JSON.stringify(this.meta)); const meta = JSON.parse(JSON.stringify(this.meta));
for (const col of meta.columns) { for (const col of meta.columns) {

9
packages/nc-gui/components/project/spreadsheet/components/virtualCell.vue

@ -13,6 +13,7 @@
:sql-ui="sqlUi" :sql-ui="sqlUi"
:is-new="isNew" :is-new="isNew"
:is-form="isForm" :is-form="isForm"
:breadcrumbs="breadcrumbs"
v-on="$listeners" v-on="$listeners"
/> />
<many-to-many-cell <many-to-many-cell
@ -28,6 +29,7 @@
:is-new="isNew" :is-new="isNew"
:api="api" :api="api"
:is-form="isForm" :is-form="isForm"
:breadcrumbs="breadcrumbs"
v-on="$listeners" v-on="$listeners"
/> />
<belongs-to-cell <belongs-to-cell
@ -44,6 +46,7 @@
:sql-ui="sqlUi" :sql-ui="sqlUi"
:is-new="isNew" :is-new="isNew"
:is-form="isForm" :is-form="isForm"
:breadcrumbs="breadcrumbs"
v-on="$listeners" v-on="$listeners"
/> />
</v-lazy> </v-lazy>
@ -65,6 +68,12 @@ export default {
hasManyCell hasManyCell
}, },
props: { props: {
breadcrumbs: {
type: Array,
default() {
return [];
}
},
column: [Object], column: [Object],
row: [Object], row: [Object],
nodes: [Object], nodes: [Object],

9
packages/nc-gui/components/project/spreadsheet/components/virtualCell/belogsToCell.vue

@ -83,6 +83,7 @@
v-model="selectedParent" v-model="selectedParent"
@cancel="selectedParent = null" @cancel="selectedParent = null"
@input="onParentSave" @input="onParentSave"
:breadcrumbs="breadcrumbs"
></component> ></component>
</v-dialog> </v-dialog>
@ -101,6 +102,12 @@ export default {
name: "belongs-to-cell", name: "belongs-to-cell",
components: {ListChildItems, ItemChip, ListItems}, components: {ListChildItems, ItemChip, ListItems},
props: { props: {
breadcrumbs: {
type: Array,
default() {
return [];
}
},
isForm: Boolean, isForm: Boolean,
value: [Object, Array], value: [Object, Array],
meta: [Object], meta: [Object],
@ -283,7 +290,7 @@ export default {
const columns = []; const columns = [];
if (this.parentMeta.columns) { if (this.parentMeta.columns) {
columns.push(...this.parentMeta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.cn)&& !((this.parentMeta.v || []).some(v => v.bt && v.bt.cn === c.cn)))) columns.push(...this.parentMeta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.cn) && !((this.parentMeta.v || []).some(v => v.bt && v.bt.cn === c.cn))))
} }
if (this.parentMeta.v) { if (this.parentMeta.v) {
columns.push(...this.parentMeta.v.map(v => ({...v, virtual: 1}))); columns.push(...this.parentMeta.v.map(v => ({...v, virtual: 1})));

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

@ -5,7 +5,7 @@
<span v-if="!isForm">{{ meta ? meta._tn : 'Children' }}</span> <span v-if="!isForm">{{ meta ? meta._tn : 'Children' }}</span>
<v-spacer> <v-spacer>
</v-spacer> </v-spacer>
<v-icon small class="mr-1" @click="loadData()">mdi-reload</v-icon>
<v-btn <v-btn
small small
class="caption" class="caption"

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

@ -14,6 +14,8 @@
class=" caption search-field ml-2" class=" caption search-field ml-2"
/> />
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-icon small class="mr-1" @click="loadData()">mdi-reload</v-icon>
<v-btn small class="caption mr-2" color="primary" @click="$emit('add-new-record')"> <v-btn small class="caption mr-2" color="primary" @click="$emit('add-new-record')">
<v-icon small>mdi-plus</v-icon>&nbsp; <v-icon small>mdi-plus</v-icon>&nbsp;
New Record New Record
@ -37,8 +39,8 @@
<span class="grey--text caption primary-key " <span class="grey--text caption primary-key "
v-if="primaryKey">(Primary Key : {{ ch[primaryKey] }})</span> v-if="primaryKey">(Primary Key : {{ ch[primaryKey] }})</span>
<v-spacer/> <v-spacer/>
<v-chip v-if="hm && ch[meta._tn]" x-small> <v-chip v-if="hm && ch[`${hm._rtn}Read`] && ch[`${hm._rtn}Read`][hmParentPrimaryValCol]" x-small>
{{ ch[primaryCol] }} {{ ch[`${hm._rtn}Read`][hmParentPrimaryValCol] }}
</v-chip> </v-chip>
</v-card-text> </v-card-text>
</v-card> </v-card>
@ -74,7 +76,7 @@ export default {
components: {Pagination}, components: {Pagination},
props: { props: {
value: Boolean, value: Boolean,
hm: Boolean, hm: [Object, Function],
title: { title: {
type: String, type: String,
default: 'Link Record' default: 'Link Record'
@ -91,7 +93,8 @@ export default {
size: Number, size: Number,
api: [Object, Function], api: [Object, Function],
mm: [Object, Function], mm: [Object, Function],
parentId: [String, Number] parentId: [String, Number],
parentMeta:[Object]
}, },
data: () => ({ data: () => ({
data: null, data: null,
@ -127,6 +130,11 @@ export default {
}, get() { }, get() {
return this.value; return this.value;
} }
},
hmParentPrimaryValCol(){
return this.hm &&
this.parentMeta &&
this.parentMeta.columns.find(v => v.pv)._cn
} }
} }
} }

10
packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue

@ -27,13 +27,14 @@
<list-items <list-items
v-if="newRecordModal" v-if="newRecordModal"
:hm="true" :hm="hm"
:size="10" :size="10"
:meta="childMeta" :meta="childMeta"
:primary-col="childPrimaryCol" :primary-col="childPrimaryCol"
:primary-key="childPrimaryKey" :primary-key="childPrimaryKey"
v-model="newRecordModal" v-model="newRecordModal"
:api="childApi" :api="childApi"
:parent-meta="meta"
@add-new-record="insertAndAddNewChildRecord" @add-new-record="insertAndAddNewChildRecord"
@add="addChildToParent" @add="addChildToParent"
:query-params="{ :query-params="{
@ -103,6 +104,7 @@
ref="expandedForm" ref="expandedForm"
:is-new.sync="isNewChild" :is-new.sync="isNewChild"
:disabled-columns="disabledChildColumns" :disabled-columns="disabledChildColumns"
:breadcrumbs="breadcrumbs"
></component> ></component>
</v-dialog> </v-dialog>
@ -132,6 +134,12 @@ export default {
listChildItemsModal listChildItemsModal
}, },
props: { props: {
breadcrumbs: {
type: Array,
default() {
return [];
}
},
value: [Object, Array], value: [Object, Array],
meta: [Object], meta: [Object],
hm: Object, hm: Object,

7
packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue

@ -95,6 +95,7 @@
ref="expandedForm" ref="expandedForm"
:is-new.sync="isNewChild" :is-new.sync="isNewChild"
v-model="selectedChild" v-model="selectedChild"
:breadcrumbs="breadcrumbs"
@cancel="selectedChild = null" @cancel="selectedChild = null"
@input="onChildSave" @input="onChildSave"
></component> ></component>
@ -119,6 +120,12 @@ export default {
name: "many-to-many-cell", name: "many-to-many-cell",
components: {ListChildItems, ItemChip, ListItems, DlgLabelSubmitCancel, listChildItemsModal}, components: {ListChildItems, ItemChip, ListItems, DlgLabelSubmitCancel, listChildItemsModal},
props: { props: {
breadcrumbs: {
type: Array,
default() {
return [];
}
},
value: [Object, Array], value: [Object, Array],
meta: [Object], meta: [Object],
mm: Object, mm: Object,

4
packages/nc-gui/components/project/spreadsheet/components/virtualHeaderCell.vue

@ -7,7 +7,7 @@
<v-icon v-else-if="column.bt" color="info" x-small class="mr-1" v-on="on">mdi-table-arrow-left</v-icon> <v-icon v-else-if="column.bt" color="info" x-small class="mr-1" v-on="on">mdi-table-arrow-left</v-icon>
<v-icon v-else-if="column.mm" color="pink" x-small class="mr-1" v-on="on">mdi-table-network</v-icon> <v-icon v-else-if="column.mm" color="pink" x-small class="mr-1" v-on="on">mdi-table-network</v-icon>
<span v-on="on" class="name" :title="column._cn">{{ column._cn }}</span> <span v-on="on" class="name flex-grow-1" :title="column._cn">{{ column._cn }}</span>
<span v-if="column.rqd" v-on="on" class="error--text text--lighten-1">&nbsp;*</span> <span v-if="column.rqd" v-on="on" class="error--text text--lighten-1">&nbsp;*</span>
</template> </template>
@ -71,6 +71,7 @@
:edit-column="true" :edit-column="true"
:column="column" :column="column"
:meta="meta" :meta="meta"
v-on="$listeners"
></edit-virtual-column> ></edit-virtual-column>
</v-menu> </v-menu>
</div> </div>
@ -149,7 +150,6 @@ export default {
<style scoped> <style scoped>
.name { .name {
max-width: calc(100px - 40px);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;

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

@ -68,7 +68,6 @@
Save Save
</x-btn> </x-btn>
<fields <fields
v-model="showFields" v-model="showFields"
:field-list="fieldList" :field-list="fieldList"
@ -136,8 +135,6 @@
</v-icon> </v-icon>
</x-btn> </x-btn>
</v-toolbar> </v-toolbar>
<div :class="`cell-height-${cellHeight}`" <div :class="`cell-height-${cellHeight}`"
style=" height:calc(100% - 32px);overflow:auto;transition: width 100ms " style=" height:calc(100% - 32px);overflow:auto;transition: width 100ms "
class="d-flex" class="d-flex"
@ -175,6 +172,7 @@
@expandRow="expandRow" @expandRow="expandRow"
@onRelationDelete="loadMeta" @onRelationDelete="loadMeta"
@loadTableData="loadTableData" @loadTableData="loadTableData"
@loadMeta="loadMeta"
></xc-grid-view> ></xc-grid-view>
</template> </template>
<template v-else-if="selectedView && selectedView.show_as === 'gallery' "> <template v-else-if="selectedView && selectedView.show_as === 'gallery' ">
@ -728,7 +726,22 @@ export default {
return o; return o;
}, {}); }, {});
const insertedData = await this.api.insert(insertObj); let insertedData = await this.api.insert(insertObj);
// todo: optimize
if (this.meta.v && this.meta.v.length) {
try {
const where = this.meta.columns.filter((c) => c.pk).map(c => `(${c._cn},eq,${insertedData[c._cn]})`).join('~and');
if (where) {
const {childs, parents, many} = this.queryParams;
const data = (await this.api.list({where, childs, parents, many}) || [insertedData]);
insertedData = data.length ? data[0] : insertedData;
}
} catch (e) {
// ignore
}
}
this.data.splice(row, 1, { this.data.splice(row, 1, {
row: insertedData, row: insertedData,
rowMeta: {}, rowMeta: {},

14
packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue

@ -14,7 +14,7 @@
:class="$store.state.windows.darkTheme ? 'grey darken-3 grey--text text--lighten-1' : 'grey lighten-4 grey--text text--darken-2'" :class="$store.state.windows.darkTheme ? 'grey darken-3 grey--text text--lighten-1' : 'grey lighten-4 grey--text text--darken-2'"
v-xc-ver-resize v-xc-ver-resize
v-for="(col,i) in availableColumns" v-for="(col,i) in availableColumns"
:key="i + '_' + col._cn" :key=" col._cn"
v-show="showFields[col._cn]" v-show="showFields[col._cn]"
@xcresize="onresize(col._cn,$event)" @xcresize="onresize(col._cn,$event)"
@xcresizing="onXcResizing(col._cn,$event)" @xcresizing="onXcResizing(col._cn,$event)"
@ -113,7 +113,7 @@
<td <td
class="cell pointer" class="cell pointer"
v-for="(columnObj,col) in availableColumns" v-for="(columnObj,col) in availableColumns"
:key="row + '_' + col + columnObj._cn" :key="row + columnObj._cn"
:class="{ :class="{
'active' : !isPublicView && selected.col === col && selected.row === row && isEditable , 'active' : !isPublicView && selected.col === col && selected.row === row && isEditable ,
'primary-column' : primaryValueColumn === columnObj._cn, 'primary-column' : primaryValueColumn === columnObj._cn,
@ -126,7 +126,7 @@
:data-col="columnObj._cn" :data-col="columnObj._cn"
> >
<virtual-cell <virtual-cell
v-if="columnObj.virtual " v-if="columnObj.virtual"
:column="columnObj" :column="columnObj"
:row="rowObj" :row="rowObj"
:nodes="nodes" :nodes="nodes"
@ -139,15 +139,9 @@
@updateCol="(...args) => updateCol(...args, columnObj.bt && meta.columns.find( c => c.cn === columnObj.bt.cn), col, row)" @updateCol="(...args) => updateCol(...args, columnObj.bt && meta.columns.find( c => c.cn === columnObj.bt.cn), col, row)"
></virtual-cell> ></virtual-cell>
<!--
<span
v-if="columnObj.virtual "
></span>
-->
<editable-cell <editable-cell
v-else-if=" v-else-if="
!isLocked !isLocked
&& !isPublicView && !isPublicView
&& (editEnabled.col === col && editEnabled.row === row) && (editEnabled.col === col && editEnabled.row === row)
|| enableEditable(columnObj) || enableEditable(columnObj)

26
packages/nc-gui/components/project/tableTabs/columns.vue

@ -122,7 +122,7 @@
v-ge="['columns','save-and-scaffold']"> v-ge="['columns','save-and-scaffold']">
<v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']"> <v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']">
<v-icon color="primary" small>mdi-json</v-icon> <v-icon color="primary" small>mdi-code-json</v-icon>
&nbsp;Save & Scaffold Module &nbsp;Save & Scaffold Module
</v-list-item-title> </v-list-item-title>
@ -132,7 +132,7 @@
v-ge="['columns','save-and-scaffold']" v-ge="['columns','save-and-scaffold']"
> >
<v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']"> <v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']">
<v-icon color="primary" small>mdi-json</v-icon> <v-icon color="primary" small>mdi-code-json</v-icon>
&nbsp;Save & Scaffold GQL Model &nbsp;Save & Scaffold GQL Model
</v-list-item-title> </v-list-item-title>
@ -144,7 +144,7 @@
v-ge="['columns','scaffold']" v-ge="['columns','scaffold']"
> >
<v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']"> <v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']">
<v-icon color="primary" small>mdi-json</v-icon> <v-icon color="primary" small>mdi-code-json</v-icon>
&nbsp;Scaffold GQL Model &nbsp;Scaffold GQL Model
</v-list-item-title> </v-list-item-title>
@ -154,7 +154,7 @@
v-ge="['columns','scaffold']" v-ge="['columns','scaffold']"
> >
<v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']"> <v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']">
<v-icon color="primary" small>mdi-json</v-icon> <v-icon color="primary" small>mdi-code-json</v-icon>
&nbsp;Scaffold GQL Resolver &nbsp;Scaffold GQL Resolver
</v-list-item-title> </v-list-item-title>
@ -164,7 +164,7 @@
v-ge="['columns','scaffold']" v-ge="['columns','scaffold']"
> >
<v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']"> <v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']">
<v-icon color="primary" small>mdi-json</v-icon> <v-icon color="primary" small>mdi-code-json</v-icon>
&nbsp;Scaffold GQL Service &nbsp;Scaffold GQL Service
</v-list-item-title> </v-list-item-title>
@ -174,7 +174,7 @@
v-ge="['columns','scaffold']" v-ge="['columns','scaffold']"
> >
<v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']"> <v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']">
<v-icon color="primary" small>mdi-json</v-icon> <v-icon color="primary" small>mdi-code-json</v-icon>
&nbsp;Scaffold GQL Relations &nbsp;Scaffold GQL Relations
</v-list-item-title> </v-list-item-title>
@ -191,7 +191,7 @@
v-ge="['columns','save-and-scaffold']" v-ge="['columns','save-and-scaffold']"
@click="saveAndScaffold()"> @click="saveAndScaffold()">
<v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']"> <v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']">
<v-icon color="primary" small>mdi-json</v-icon> <v-icon color="primary" small>mdi-code-json</v-icon>
&nbsp;Save & Scaffold Module &nbsp;Save & Scaffold Module
</v-list-item-title> </v-list-item-title>
@ -201,7 +201,7 @@
v-ge="['columns','save-and-scaffold']" v-ge="['columns','save-and-scaffold']"
@click="saveAndScaffoldModel()"> @click="saveAndScaffoldModel()">
<v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']"> <v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']">
<v-icon color="primary" small>mdi-json</v-icon> <v-icon color="primary" small>mdi-code-json</v-icon>
&nbsp;Save & Scaffold Model &nbsp;Save & Scaffold Model
</v-list-item-title> </v-list-item-title>
@ -211,7 +211,7 @@
v-ge="['columns','json-to-column']" v-ge="['columns','json-to-column']"
@click="scaffold({model:true})"> @click="scaffold({model:true})">
<v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']"> <v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']">
<v-icon color="primary" small>mdi-json</v-icon> <v-icon color="primary" small>mdi-code-json</v-icon>
&nbsp;Scaffold Model &nbsp;Scaffold Model
</v-list-item-title> </v-list-item-title>
@ -221,7 +221,7 @@
@click="scaffold({router:true})" @click="scaffold({router:true})"
> >
<v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']"> <v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']">
<v-icon color="primary" small>mdi-json</v-icon> <v-icon color="primary" small>mdi-code-json</v-icon>
&nbsp;Scaffold Router &nbsp;Scaffold Router
</v-list-item-title> </v-list-item-title>
@ -231,7 +231,7 @@
@click="scaffold({service:true})" @click="scaffold({service:true})"
> >
<v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']"> <v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']">
<v-icon color="primary" small>mdi-json</v-icon> <v-icon color="primary" small>mdi-code-json</v-icon>
&nbsp;Scaffold Service &nbsp;Scaffold Service
</v-list-item-title> </v-list-item-title>
@ -241,7 +241,7 @@
@click="scaffold({relations:true})" @click="scaffold({relations:true})"
> >
<v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']"> <v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']">
<v-icon color="primary" small>mdi-json</v-icon> <v-icon color="primary" small>mdi-code-json</v-icon>
&nbsp;Scaffold Relations &nbsp;Scaffold Relations
</v-list-item-title> </v-list-item-title>
@ -255,7 +255,7 @@
@click="showJsonToColumDlg = true" @click="showJsonToColumDlg = true"
> >
<v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']"> <v-list-item-title class="font-weight-bold pa-1" v-ge="['columns','json-to-columns']">
<v-icon color="primary" small>mdi-json</v-icon> <v-icon color="primary" small>mdi-code-json</v-icon>
&nbsp; &nbsp;
JSON To Columns JSON To Columns
</v-list-item-title> </v-list-item-title>

4
packages/nc-gui/components/project/xcInfo.vue

@ -187,7 +187,7 @@
<v-card-text class="pb-0 font-weight-bold" v-else-if="item.type==='graphql'">Graphql Endpoint <v-card-text class="pb-0 font-weight-bold" v-else-if="item.type==='graphql'">Graphql Endpoint
</v-card-text> </v-card-text>
<v-card-text class="title white--text "> <v-card-text class="title white--text ">
<v-icon color="success" class=" mr-1">mdi-json</v-icon> <v-icon color="success" class=" mr-1">mdi-code-json</v-icon>
<a :href="`${origin}${item.apiEndpoint}`" target="_blank">{{ `${origin}${item.apiEndpoint}` }}</a> <a :href="`${origin}${item.apiEndpoint}`" target="_blank">{{ `${origin}${item.apiEndpoint}` }}</a>
</v-card-text> </v-card-text>
@ -224,7 +224,7 @@ export default {
info: null, info: null,
aggregatedInfo: null, aggregatedInfo: null,
apiTypeIcon: { apiTypeIcon: {
'rest': {icon: 'mdi-json', iconColor: 'green'}, 'rest': {icon: 'mdi-code-json', iconColor: 'green'},
'graphql': {icon: 'mdi-graphql', iconColor: 'pink'}, 'graphql': {icon: 'mdi-graphql', iconColor: 'pink'},
'grpc': {src: 'grpc-icon-color.png', type: 'img'}, 'grpc': {src: 'grpc-icon-color.png', type: 'img'},
}, },

2
packages/nc-gui/components/utils/dlgProjectCreate.vue

@ -76,7 +76,7 @@ export default {
loading: false, loading: false,
projectType: 'rest', projectType: 'rest',
projectTypes: [ projectTypes: [
{text: 'Automatic REST APIs on database', value: 'rest', icon: 'mdi-json', iconColor: 'green'}, {text: 'Automatic REST APIs on database', value: 'rest', icon: 'mdi-code-json', iconColor: 'green'},
{text: 'Automatic GRAPHQL APIs on database', value: 'graphql', icon: 'mdi-graphql', iconColor: 'pink'}, {text: 'Automatic GRAPHQL APIs on database', value: 'graphql', icon: 'mdi-graphql', iconColor: 'pink'},
/* { /* {
text: 'Automatic gRPC APIs on database', text: 'Automatic gRPC APIs on database',

12
packages/nc-gui/helpers/treeViewIcons.js

@ -73,17 +73,17 @@ export default {
}, },
apiClientDir: { apiClientDir: {
class: '', class: '',
icon: "mdi-json", icon: "mdi-code-json",
color: "warning", color: "warning",
openColor: "", openColor: "",
openIcon: "mdi-json" openIcon: "mdi-code-json"
}, },
apiClientSwaggerDir: { apiClientSwaggerDir: {
class: '', class: '',
icon: "mdi-json", icon: "mdi-code-json",
color: "warning", color: "warning",
openColor: "", openColor: "",
openIcon: "mdi-json" openIcon: "mdi-code-json"
}, },
sqlClientDir: { sqlClientDir: {
class: '', class: '',
@ -199,10 +199,10 @@ export default {
}, },
projectSettings: { projectSettings: {
class: '', class: '',
icon: "mdi-settings", icon: "mdi-cog",
color: "", color: "",
openColor: "", openColor: "",
openIcon: "mdi-settings" openIcon: "mdi-cog"
}, },
cronJobs: { cronJobs: {
class: '', class: '',

4
packages/nc-gui/helpers/treeViewIconsColors.js

@ -56,10 +56,10 @@ export default {
openIcon: "mdi-alpha-a-circle-outline" openIcon: "mdi-alpha-a-circle-outline"
}, },
apiClientDir: { apiClientDir: {
icon: "mdi-json", icon: "mdi-code-json",
color: "", color: "",
openColor: "green", openColor: "green",
openIcon: "mdi-json" openIcon: "mdi-code-json"
}, },
sqlClientDir: { sqlClientDir: {
icon: "mdi-alpha-s-circle-outline", icon: "mdi-alpha-s-circle-outline",

22
packages/nc-gui/layouts/default.vue

@ -346,7 +346,7 @@
<!-- <v-icon tooltip="GraphQL Client (^⇧G)"--> <!-- <v-icon tooltip="GraphQL Client (^⇧G)"-->
<!-- v-if="isRest"--> <!-- v-if="isRest"-->
<!-- @click="swaggerClientTabAdd()" class="ml-3 " size="20">--> <!-- @click="swaggerClientTabAdd()" class="ml-3 " size="20">-->
<!-- mdi-json--> <!-- mdi-code-json-->
<!-- </v-icon>--> <!-- </v-icon>-->
<!-- &nbsp;--> <!-- &nbsp;-->
<!-- GraphiQL--> <!-- GraphiQL-->
@ -377,7 +377,7 @@
<v-icon class="ml-3 " <v-icon class="ml-3 "
v-if="!$store.state.windows.nc" size="20" @click="apiClientSwaggerTabAdd()" v-if="!$store.state.windows.nc" size="20" @click="apiClientSwaggerTabAdd()"
tooltip="API Client (^⇧A)">mdi-json tooltip="API Client (^⇧A)">mdi-code-json
</v-icon> </v-icon>
<!-- &nbsp; REST Client--> <!-- &nbsp; REST Client-->
<!-- </x-btn>--> <!-- </x-btn>-->
@ -472,16 +472,16 @@
<template v-else> <template v-else>
<!-- <x-icon iconClass="mr-4" @click="apiClientSwaggerOpen()" v-if="!$store.state.windows.isComp"--> <!-- <x-icon iconClass="mr-4" @click="apiClientSwaggerOpen()" v-if="!$store.state.windows.isComp"-->
<!-- tooltip="API Client (^⇧A)">mdi-json--> <!-- tooltip="API Client (^⇧A)">mdi-code-json-->
<!-- </x-icon>--> <!-- </x-icon>-->
<!-- <x-icon iconClass="mr-4" tooltip="Feed (^⇧F)" @click="feedDialog = true">mdi-glasses</x-icon>--> <!-- <x-icon iconClass="mr-4" tooltip="Feed (^⇧F)" @click="feedDialog = true">mdi-glasses</x-icon>-->
<!-- <span v-shortkey="['ctrl','shift','f']" @shortkey="feedDialog = true"></span>--> <!-- <span v-shortkey="['ctrl','shift','f']" @shortkey="feedDialog = true"></span>-->
<!-- <x-icon iconClass="mr-4" @click="settingsTabAdd" size="20" tooltip="Tool Settings (^⇧C)">mdi-settings--> <!-- <x-icon iconClass="mr-4" @click="settingsTabAdd" size="20" tooltip="Tool Settings (^⇧C)">mdi-cog-->
<!-- </x-icon>--> <!-- </x-icon>-->
<!-- <x-icon iconClass="mr-4" @click="settingsDialog = true" tooltip="Tool Settings (^⇧C)">mdi-settings--> <!-- <x-icon iconClass="mr-4" @click="settingsDialog = true" tooltip="Tool Settings (^⇧C)">mdi-cog-->
<!-- </x-icon>--> <!-- </x-icon>-->
<!-- <span v-shortkey="[ 'ctrl','shift', 'c']"--> <!-- <span v-shortkey="[ 'ctrl','shift', 'c']"-->
<!-- @shortkey="settingsDialog = true"></span> --> <!-- @shortkey="settingsDialog = true"></span> -->
@ -546,10 +546,10 @@
@shortkey="terminalTabAdd()" tooltip="Terminal"></span> @shortkey="terminalTabAdd()" tooltip="Terminal"></span>
<!-- <x-icon key="settings-dash" iconClass="mr-1 ml-4" @click="settingsDialog = true" tooltip="Tool Settings (^⇧C)">--> <!-- <x-icon key="settings-dash" iconClass="mr-1 ml-4" @click="settingsDialog = true" tooltip="Tool Settings (^⇧C)">-->
<!-- mdi-settings--> <!-- mdi-cog-->
<!-- </x-icon>--> <!-- </x-icon>-->
<!-- <x-icon key="settings-dash" iconClass="mr-1 ml-4" size="20" @click="settingsTabAdd" tooltip="Tool Settings (^⇧C)">--> <!-- <x-icon key="settings-dash" iconClass="mr-1 ml-4" size="20" @click="settingsTabAdd" tooltip="Tool Settings (^⇧C)">-->
<!-- mdi-settings--> <!-- mdi-cog-->
<!-- </x-icon>--> <!-- </x-icon>-->
<notification class="mx-2"></notification> <notification class="mx-2"></notification>
@ -570,7 +570,7 @@
<v-list dense> <v-list dense>
<v-list-item dense to="/user/settings" v-if="userAuthIsEmail"> <v-list-item dense to="/user/settings" v-if="userAuthIsEmail">
<v-list-item-title v-ge="['Settings','']"> <v-list-item-title v-ge="['Settings','']">
<v-icon small>mdi-settings</v-icon> &nbsp; <span class="font-weight-regular">Settings</span> <v-icon small>mdi-cog</v-icon> &nbsp; <span class="font-weight-regular">Settings</span>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
<v-divider v-if="userAuthIsEmail"></v-divider> <v-divider v-if="userAuthIsEmail"></v-divider>
@ -630,7 +630,7 @@
<v-list-item-title> <v-list-item-title>
<v-icon small key="terminal-dash"> <v-icon small key="terminal-dash">
{{ isGql ? 'mdi-graphql' : 'mdi-json' }} {{ isGql ? 'mdi-graphql' : 'mdi-code-json' }}
</v-icon>&nbsp; </v-icon>&nbsp;
<span class="font-weight-regular"> <span class="font-weight-regular">
{{ isGql ? 'GraphQL APIs' : 'Swagger APIs Doc' }}</span> {{ isGql ? 'GraphQL APIs' : 'Swagger APIs Doc' }}</span>
@ -642,7 +642,7 @@
<v-list-item-title> <v-list-item-title>
<v-icon small key="terminal-dash"> <v-icon small key="terminal-dash">
mdi-settings mdi-cog
</v-icon>&nbsp; </v-icon>&nbsp;
<span class="font-weight-regular">Themes</span> <span class="font-weight-regular">Themes</span>
@ -748,7 +748,7 @@
<!-- to="/client"--> <!-- to="/client"-->
<!-- >--> <!-- >-->
<!-- <v-list-item-icon>--> <!-- <v-list-item-icon>-->
<!-- <v-icon>mdi-json</v-icon>--> <!-- <v-icon>mdi-code-json</v-icon>-->
<!-- </v-list-item-icon>--> <!-- </v-list-item-icon>-->
<!-- <v-list-item-content>--> <!-- <v-list-item-content>-->

14
packages/nc-gui/nuxt.config.js

@ -192,14 +192,12 @@ export default {
return config; return config;
} }
}, },
loading: false loading: {
// { color: '#13f4ef',
// color: '#13f4ef', height: '0px',
// height: '0px', continuous: true,
// continuous: true, duration: 3000
// duration: 3000 },
// }
,
css: [ css: [
// '@/assets/style/fonts.css', // '@/assets/style/fonts.css',
'@/assets/css/global.css', '@/assets/css/global.css',

2
packages/nc-gui/pages/project/id.vue

@ -590,7 +590,7 @@ export default {
{text: "Disabled", value: "none"}, {text: "Disabled", value: "none"},
], ],
projectTypes: [ projectTypes: [
{text: 'Automatic REST APIs on database', value: 'rest', icon: 'mdi-json', iconColor: 'green'}, {text: 'Automatic REST APIs on database', value: 'rest', icon: 'mdi-code-json', iconColor: 'green'},
{text: 'Automatic GRAPHQL APIs on database', value: 'graphql', icon: 'mdi-graphql', iconColor: 'pink'}, {text: 'Automatic GRAPHQL APIs on database', value: 'graphql', icon: 'mdi-graphql', iconColor: 'pink'},
{text: 'Automatic gRPC APIs on database', value: 'grpc', icon: 'grpc-icon-color.png', type: 'img'}, {text: 'Automatic gRPC APIs on database', value: 'grpc', icon: 'grpc-icon-color.png', type: 'img'},
// { // {

2
packages/nc-gui/pages/project/name.vue

@ -66,7 +66,7 @@ export default {
loading: false, loading: false,
projectType: 'rest', projectType: 'rest',
projectTypes: [ projectTypes: [
{text: 'Automatic REST APIs on database', value: 'rest', icon: 'mdi-json', iconColor: 'green'}, {text: 'Automatic REST APIs on database', value: 'rest', icon: 'mdi-code-json', iconColor: 'green'},
{text: 'Automatic GRAPHQL APIs on database', value: 'graphql', icon: 'mdi-graphql', iconColor: 'pink'}, {text: 'Automatic GRAPHQL APIs on database', value: 'graphql', icon: 'mdi-graphql', iconColor: 'pink'},
{ {
text: 'Automatic gRPC APIs on database', text: 'Automatic gRPC APIs on database',

2
packages/nc-gui/pages/project/xcdb.vue

@ -112,7 +112,7 @@ export default {
loading: false, loading: false,
projectType: 'rest', projectType: 'rest',
projectTypes: [ projectTypes: [
{text: 'REST APIs', value: 'rest', icon: 'mdi-json', iconColor: 'green'}, {text: 'REST APIs', value: 'rest', icon: 'mdi-code-json', iconColor: 'green'},
{text: 'GRAPHQL APIs', value: 'graphql', icon: 'mdi-graphql', iconColor: 'pink'}, {text: 'GRAPHQL APIs', value: 'graphql', icon: 'mdi-graphql', iconColor: 'pink'},
/* { /* {
text: 'Automatic gRPC APIs on database', text: 'Automatic gRPC APIs on database',

2
packages/nc-gui/pages/projects/index.vue

@ -241,7 +241,7 @@
{{ {{
props.item.projectType === 'graphql' props.item.projectType === 'graphql'
? 'mdi-graphql' ? 'mdi-graphql'
: 'mdi-json' : 'mdi-code-json'
}} }}
</x-icon> </x-icon>

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

@ -391,12 +391,15 @@ export const actions = {
// 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 (['relationCreate', if ([
'xcModelSet',
'relationCreate',
'xcM2MRelationCreate', 'xcM2MRelationCreate',
'xcVirtualRelationCreate', 'xcVirtualRelationCreate',
'relationDelete', 'relationDelete',
'xcVirtualRelationDelete', 'xcVirtualRelationDelete',
'xcRelationColumnDelete'].includes(op)) { 'xcRelationColumnDelete'
].includes(op)) {
commit('meta/MutClear', null, {root: true}) commit('meta/MutClear', null, {root: true})
} }

42
packages/nocodb/package-lock.json generated

@ -4456,9 +4456,9 @@
} }
}, },
"trim-newlines": { "trim-newlines": {
"version": "3.0.0", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz",
"integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==",
"dev": true "dev": true
}, },
"type-fest": { "type-fest": {
@ -4760,9 +4760,9 @@
} }
}, },
"trim-newlines": { "trim-newlines": {
"version": "3.0.0", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz",
"integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==",
"dev": true "dev": true
}, },
"type-fest": { "type-fest": {
@ -11146,8 +11146,7 @@
}, },
"@azure/ms-rest-js": { "@azure/ms-rest-js": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-1.9.0.tgz", "resolved": "",
"integrity": "sha512-cB4Z2Mg7eBmet1rfbf0QSO1XbhfknRW7B+mX3IHJq0KGHaGJvCPoVTgdsJdCkazEMK1jtANFNEDDzSQacxyzbA==",
"requires": { "requires": {
"@types/tunnel": "0.0.0", "@types/tunnel": "0.0.0",
"axios": "^0.19.0", "axios": "^0.19.0",
@ -16458,8 +16457,7 @@
}, },
"glob-parent": { "glob-parent": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", "resolved": "",
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
"requires": { "requires": {
"is-glob": "^4.0.1" "is-glob": "^4.0.1"
} }
@ -16859,8 +16857,7 @@
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", "resolved": ""
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
}, },
"interpret": { "interpret": {
"version": "2.2.0", "version": "2.2.0",
@ -17772,8 +17769,7 @@
}, },
"lodash": { "lodash": {
"version": "4.17.20", "version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", "resolved": ""
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
}, },
"lodash._reinterpolate": { "lodash._reinterpolate": {
"version": "3.0.0", "version": "3.0.0",
@ -22572,8 +22568,7 @@
}, },
"underscore": { "underscore": {
"version": "1.12.0", "version": "1.12.0",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.0.tgz", "resolved": ""
"integrity": "sha512-21rQzss/XPMjolTiIezSu3JAjgagXKROtNrYFEOWK109qY1Uv2tVjPTZ1ci2HgvQDA16gHYSthQIJfB+XId/rQ=="
}, },
"unicode-canonical-property-names-ecmascript": { "unicode-canonical-property-names-ecmascript": {
"version": "1.0.4", "version": "1.0.4",
@ -23597,8 +23592,7 @@
}, },
"xmldom": { "xmldom": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.4.0.tgz", "resolved": ""
"integrity": "sha512-2E93k08T30Ugs+34HBSTQLVtpi6mCddaY8uO+pMNk1pqSjV5vElzn4mmh6KLxN3hki8rNcHSYzILoh3TEWORvA=="
}, },
"xpath.js": { "xpath.js": {
"version": "1.1.0", "version": "1.1.0",
@ -24316,9 +24310,9 @@
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
}, },
"normalize-url": { "normalize-url": {
"version": "4.5.0", "version": "4.5.1",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
"dev": true "dev": true
}, },
"npm-bundled": { "npm-bundled": {
@ -30655,9 +30649,9 @@
} }
}, },
"ws": { "ws": {
"version": "7.4.5", "version": "7.4.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==" "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A=="
}, },
"xc-core-ts": { "xc-core-ts": {
"version": "0.1.0", "version": "0.1.0",

3
packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts

@ -1271,7 +1271,7 @@ class BaseModelSql extends BaseModel {
* Gets parent list along with children list and parent * Gets parent list along with children list and parent
* *
* @param {Object} args * @param {Object} args
* @param {String} args.childs - comma separated child table names * @param {String} args.childs - comma separated child table naes
* @param {String} [args.fields=*] - commas separated column names of this table * @param {String} [args.fields=*] - commas separated column names of this table
* @param {String} [args.fields*=*] - commas separated column names of child table(* is a natural number 'i' where i is index of child table in comma separated list) * @param {String} [args.fields*=*] - commas separated column names of child table(* is a natural number 'i' where i is index of child table in comma separated list)
* @param {String} [args.where] - where clause with conditions within () * @param {String} [args.where] - where clause with conditions within ()
@ -1286,6 +1286,7 @@ class BaseModelSql extends BaseModel {
*/ */
// todo : add conditionGraph // todo : add conditionGraph
// todo : implement nestedread
async nestedList({childs = '', parents = '', many = '', where, fields: fields1, f, ...rest}) { async nestedList({childs = '', parents = '', many = '', where, fields: fields1, f, ...rest}) {
let fields = fields1 || f || '*'; let fields = fields1 || f || '*';
try { try {

4
packages/nocodb/src/lib/noco/NcProjectBuilder.ts

@ -224,6 +224,10 @@ export default class NcProjectBuilder {
await curBuilder.onValidationUpdate(data.req.args.tn); await curBuilder.onValidationUpdate(data.req.args.tn);
console.log(`Updated validations for table : ${data.req.args.tn}`) console.log(`Updated validations for table : ${data.req.args.tn}`)
break; break;
case 'xcUpdateVirtualKeyAlias':
await curBuilder.onVirtualColumnAliasUpdate(data.req.args.tn);
console.log(`Updated validations for table : ${data.req.args.tn}`)
break;
case 'xcModelSchemaSet': case 'xcModelSchemaSet':
await curBuilder.onGqlSchemaUpdate(data.req.args.tn, data.req.args.schema); await curBuilder.onGqlSchemaUpdate(data.req.args.tn, data.req.args.schema);

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

@ -322,7 +322,8 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
this.models[modelRow.title] = this.getBaseModel(metaObj); this.models[modelRow.title] = this.getBaseModel(metaObj);
// todo: check tableAlias changed or not // todo: check tableAlias changed or not
await this.onTableRename(tn, tn) // todo:
// await this.onTableRename(tn, tn)
} }
@ -1213,9 +1214,10 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
tableMetaA.hasMany.splice(tableMetaA.hasMany.findIndex(hm => hm.tn === meta.tn), 1) tableMetaA.hasMany.splice(tableMetaA.hasMany.findIndex(hm => hm.tn === meta.tn), 1)
tableMetaB.hasMany.splice(tableMetaB.hasMany.findIndex(hm => hm.tn === meta.tn), 1)*/ tableMetaB.hasMany.splice(tableMetaB.hasMany.findIndex(hm => hm.tn === meta.tn), 1)*/
// add manytomany data under metadata of both related columns
// add manytomany data under metadata of both linked tables
tableMetaA.manyToMany = tableMetaA.manyToMany || []; tableMetaA.manyToMany = tableMetaA.manyToMany || [];
if (tableMetaA.manyToMany.every(mm => mm.vtn === meta.vtn)) { if (tableMetaA.manyToMany.every(mm => mm.vtn !== meta.vtn)) {
tableMetaA.manyToMany.push({ tableMetaA.manyToMany.push({
"tn": tableMetaA.tn, "tn": tableMetaA.tn,
"cn": meta.belongsTo[0].rcn, "cn": meta.belongsTo[0].rcn,
@ -1232,7 +1234,7 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
metas.add(tableMetaA) metas.add(tableMetaA)
} }
tableMetaB.manyToMany = tableMetaB.manyToMany || []; tableMetaB.manyToMany = tableMetaB.manyToMany || [];
if (tableMetaB.manyToMany.every(mm => mm.vtn === meta.vtn)) { if (tableMetaB.manyToMany.every(mm => mm.vtn !== meta.vtn)) {
tableMetaB.manyToMany.push({ tableMetaB.manyToMany.push({
"tn": tableMetaB.tn, "tn": tableMetaB.tn,
"cn": meta.belongsTo[1].rcn, "cn": meta.belongsTo[1].rcn,
@ -1719,6 +1721,12 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
await this.getManyToManyRelations({localMetas: metas}); await this.getManyToManyRelations({localMetas: metas});
return metas; return metas;
} }
public async onVirtualColumnAliasUpdate(tableName: string): Promise<void> {
const model = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {title: tableName});
const meta = JSON.parse(model.meta);
this.models[tableName] = this.getBaseModel(meta);
}
} }
export {IGNORE_TABLES}; export {IGNORE_TABLES};

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

@ -147,7 +147,7 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
public async onTableCreate(tn: string, args): Promise<void> { public async onTableCreate(tn: string, args): Promise<void> {
this.log(`onTableCreate : '%s' `, tn) this.log(`onTableCreate : '%s' `, tn)
const columns = { const columns = {
[tn]: args.columns.map(({altered: _al, ...rest}) => rest) [tn]: args?.columns?.map(({altered: _al, ...rest}) => rest)
} }
@ -176,6 +176,7 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
await this.reInitializeGraphqlEndpoint(); await this.reInitializeGraphqlEndpoint();
} }
// todo: m2m
public async onTableRename(oldTableName: string, newTableName: string): Promise<void> { public async onTableRename(oldTableName: string, newTableName: string): Promise<void> {
this.log(`onTableRename : '%s' => '%s' `, oldTableName, newTableName) this.log(`onTableRename : '%s' => '%s' `, oldTableName, newTableName)

4
packages/nocodb/src/lib/noco/gql/GqlResolver.ts

@ -39,8 +39,8 @@ export default class GqlResolver extends GqlBaseResolver {
return this.models?.[this.table]; return this.models?.[this.table];
} }
public async list(args, {req, res}): Promise<any> { public async list(args, {req, res}: { req: any & { model: BaseModelSql }, res: any }): Promise<any> {
const startTime = process.hrtime(); const startTime = process.hrtime();
try { try {
if (args.conditionGraph && typeof args.conditionGraph === 'string') { if (args.conditionGraph && typeof args.conditionGraph === 'string') {
args.conditionGraph = {models: this.models, condition: JSON.parse(args.conditionGraph)} args.conditionGraph = {models: this.models, condition: JSON.parse(args.conditionGraph)}

256
packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts

@ -1364,6 +1364,10 @@ export default class NcMetaMgr {
result = await this.xcModelSet(args); result = await this.xcModelSet(args);
break; break;
case 'xcUpdateVirtualKeyAlias':
result = await this.xcUpdateVirtualKeyAlias(args);
break;
case 'xcRelationsGet': case 'xcRelationsGet':
result = await this.xcRelationsGet(args); result = await this.xcRelationsGet(args);
break; break;
@ -1857,6 +1861,42 @@ export default class NcMetaMgr {
}); });
} }
protected async xcUpdateVirtualKeyAlias(args): Promise<any> {
const dbAlias = await this.getDbAlias(args);
const model = await this.xcMeta.metaGet(args.project_id, dbAlias, 'nc_models', {
title: args.args.tn
});
const meta = JSON.parse(model.meta);
const vColumn = meta.v.find(v => v._cn === args.args.oldAlias);
if (!vColumn) {
return
}
vColumn._cn = args.args.newAlias;
const queryParams = JSON.parse(model.query_params);
if (queryParams?.showFields && args.args.oldAlias in queryParams.showFields) {
queryParams.showFields[args.args.newAlias] = queryParams.showFields[args.args.oldAlias];
}
if (queryParams?.columnsWidth && args.args.oldAlias in queryParams.columnsWidth) {
queryParams.columnsWidth[args.args.newAlias] = queryParams.columnsWidth[args.args.oldAlias];
}
if (queryParams?.fieldsOrder) {
queryParams.fieldsOrder.map(v => v === args.args.oldAlias ? args.args.newAlias : v)
}
await this.xcMeta.metaUpdate(args.project_id, dbAlias, 'nc_models', {
meta: JSON.stringify(meta),
query_params: JSON.stringify(queryParams),
}, {
title: args.args.tn
});
this.cacheModelDel(args.project_id, dbAlias, 'table', args.args.tn);
}
// NOTE: updated // NOTE: updated
protected async xcRelationsGet(args): Promise<any> { protected async xcRelationsGet(args): Promise<any> {
const dbAlias = await this.getDbAlias(args); const dbAlias = await this.getDbAlias(args);
@ -2461,120 +2501,73 @@ export default class NcMetaMgr {
protected async xcRelationColumnDelete(args: any, req, deleteColumn = true): Promise<any> { protected async xcRelationColumnDelete(args: any, req, deleteColumn = true): Promise<any> {
// this.xcMeta.startTransaction(); // this.xcMeta.startTransaction();
// try { // try {
const dbAlias = this.getDbAlias(args); const dbAlias = this.getDbAlias(args);
const projectId = this.getProjectId(args); const projectId = this.getProjectId(args);
// const parent = await this.xcMeta.metaGet(projectId, dbAlias, 'nc_models', { // const parent = await this.xcMeta.metaGet(projectId, dbAlias, 'nc_models', {
// title: args.args.parentTable // title: args.args.parentTable
// }); // });
// // @ts-ignore // // @ts-ignore
// const parentMeta = JSON.parse(parent.meta); // const parentMeta = JSON.parse(parent.meta);
// @ts-ignore // @ts-ignore
// todo: compare column // todo: compare column
switch (args.args.type) { switch (args.args.type) {
case 'bt': case 'bt':
case 'hm': case 'hm':
const child = await this.xcMeta.metaGet(projectId, dbAlias, 'nc_models', { const child = await this.xcMeta.metaGet(projectId, dbAlias, 'nc_models', {
title: args.args.childTable title: args.args.childTable
}); });
const childMeta = JSON.parse(child.meta); const childMeta = JSON.parse(child.meta);
const relation = childMeta.belongsTo.find(bt => bt.rtn === args.args.parentTable); const relation = childMeta.belongsTo.find(bt => bt.rtn === args.args.parentTable);
// todo: virtual relation delete // todo: virtual relation delete
if (relation) { if (relation) {
const opArgs = { const opArgs = {
...args,
args: {
childColumn: relation.cn,
childTable: relation.tn,
parentTable: relation.rtn,
parentColumn: relation.rcn
},
api: 'relationDelete',
sqlOpPlus: true,
};
let out;
if (relation?.type === 'virtual') {
opArgs.api = 'xcVirtualRelationDelete';
out = await this.xcVirtualRelationDelete(opArgs, req);
} else {
out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('relationDelete', opArgs);
}
if (this.listener) {
await this.listener({
req: opArgs,
res: out,
user: req.user,
ctx: {req}
});
}
}
if (deleteColumn) {
const originalColumns = childMeta.columns;
const columns = childMeta.columns.map(c => ({
...c, ...(relation.cn === c.cn ? {
altered: 4,
cno: c.cn
} : {cno: c.cn})
}))
const opArgs = {
...args,
args: {
columns,
originalColumns,
tn: childMeta.tn,
},
sqlOpPlus: true,
api: 'tableUpdate'
}
const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableUpdate', opArgs);
if (this.listener) {
await this.listener({
req: opArgs,
res: out,
user: req.user,
ctx: {req}
});
}
}
break;
case 'mm': {
const assoc = await this.xcMeta.metaGet(projectId, dbAlias, 'nc_models', {
title: args.args.assocTable
});
const assocMeta = JSON.parse(assoc.meta);
const rel1 = assocMeta.belongsTo.find(bt => bt.rtn === args.args.parentTable)
const rel2 = assocMeta.belongsTo.find(bt => bt.rtn === args.args.childTable)
await this.xcRelationColumnDelete({
...args,
args: {
parentTable: rel1.rtn,
parentColumn: rel1.rcn,
childTable: rel1.tn,
childColumn: rel1.cn,
type: 'bt',
}
}, req, false)
await this.xcRelationColumnDelete({
...args, ...args,
args: { args: {
parentTable: rel2.rtn, childColumn: relation.cn,
parentColumn: rel2.rcn, childTable: relation.tn,
childTable: rel2.tn, parentTable: relation.rtn,
childColumn: rel2.cn, parentColumn: relation.rcn
type: 'bt', },
} api: 'relationDelete',
}, req, false); sqlOpPlus: true,
};
let out;
if (relation?.type === 'virtual') {
opArgs.api = 'xcVirtualRelationDelete';
out = await this.xcVirtualRelationDelete(opArgs, req);
} else {
out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('relationDelete', opArgs);
}
if (this.listener) {
await this.listener({
req: opArgs,
res: out,
user: req.user,
ctx: {req}
});
}
}
if (deleteColumn) {
const originalColumns = childMeta.columns;
const columns = childMeta.columns.map(c => ({
...c, ...(relation.cn === c.cn ? {
altered: 4,
cno: c.cn
} : {cno: c.cn})
}))
const opArgs = { const opArgs = {
...args, ...args,
args: assocMeta, args: {
api: 'tableDelete', columns,
originalColumns,
tn: childMeta.tn,
},
sqlOpPlus: true, sqlOpPlus: true,
}; api: 'tableUpdate'
const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableDelete', opArgs); }
const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableUpdate', opArgs);
if (this.listener) { if (this.listener) {
await this.listener({ await this.listener({
@ -2584,10 +2577,57 @@ export default class NcMetaMgr {
ctx: {req} ctx: {req}
}); });
} }
}
break;
case 'mm': {
const assoc = await this.xcMeta.metaGet(projectId, dbAlias, 'nc_models', {
title: args.args.assocTable
});
const assocMeta = JSON.parse(assoc.meta);
const rel1 = assocMeta.belongsTo.find(bt => bt.rtn === args.args.parentTable)
const rel2 = assocMeta.belongsTo.find(bt => bt.rtn === args.args.childTable)
await this.xcRelationColumnDelete({
...args,
args: {
parentTable: rel1.rtn,
parentColumn: rel1.rcn,
childTable: rel1.tn,
childColumn: rel1.cn,
type: 'bt',
}
}, req, false)
await this.xcRelationColumnDelete({
...args,
args: {
parentTable: rel2.rtn,
parentColumn: rel2.rcn,
childTable: rel2.tn,
childColumn: rel2.cn,
type: 'bt',
}
}, req, false);
const opArgs = {
...args,
args: assocMeta,
api: 'tableDelete',
sqlOpPlus: true,
};
const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableDelete', opArgs);
if (this.listener) {
await this.listener({
req: opArgs,
res: out,
user: req.user,
ctx: {req}
});
} }
break;
} }
break;
}
// this.xcMeta.commit() // this.xcMeta.commit()
// } catch (e) { // } catch (e) {
// this.xcMeta.rollback(e) // this.xcMeta.rollback(e)

2
packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMysql.ts

@ -233,7 +233,7 @@ class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema {
break; break;
case "set": case "set":
return "[String]"; return "JSON";
break; break;
case "geometry": case "geometry":

Loading…
Cancel
Save