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>
<template v-slot:activator="{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>
</div>
</template>

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

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

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

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

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

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

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

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

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

@ -8,7 +8,7 @@
<v-spacer></v-spacer>
<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 cols="12">
<v-text-field
@ -57,12 +57,22 @@ export default {
},
async save() {
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) {
console.log(e)
this.$toast.error('Failed to update column alias').goAway(3000);
}
this.$emit('close');
this.$emit('saved');
this.$emit('input', false);
},
focusInput() {

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

@ -23,10 +23,10 @@ export default {
computed: {
localState: {
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) {
this.$emit('input', new Date(val).toJSON().slice(0, 10));
this.$emit('input', val && new Date(val).toJSON().slice(0, 10));
}
},
parentListeners() {
@ -48,7 +48,7 @@ export default {
<style scoped>
.value {
width: 100%;
min-height:20px;
min-height: 20px;
}
</style>

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

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

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

@ -1,11 +1,11 @@
<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>-->
<template v-slot:selection="{item}">
<div class="d-100 pl-4" :class="{
<div class="d-100" :class="{
'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>
</template>
<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
},
set(val) {
this.$emit('input', val);
this.$emit('input', +val);
}
},
parentListeners(){

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

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

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

@ -1,5 +1,8 @@
<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.shift.enter.stop
></textarea>
@ -48,7 +51,7 @@ export default {
input, textarea {
width: 100%;
min-height: 60px;
height: calc(100% - 28px);
height: 100%;
color: var(--v-textColor-base);
}
</style>

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

@ -33,10 +33,15 @@
</div>
</v-toolbar>
<div class="form-container ">
<v-card-text class=" py-0 px-0 " :class="{
'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-row class="h-100">
@ -80,6 +85,7 @@
:sql-ui="sqlUi"
:is-new="isNew"
:is-form="true"
:breadcrumbs="localBreadcrumbs"
@updateCol="updateCol"
@newRecordsSaved="$listeners.loadTableData || (() => {})"
></virtual-cell>
@ -208,6 +214,12 @@ export default {
components: {VirtualHeaderCell, VirtualCell, EditableCell, HeaderCell},
mixins: [colors],
props: {
breadcrumbs: {
type: Array,
default() {
return [];
}
},
dbAlias: String,
value: Object,
meta: Object,
@ -398,6 +410,9 @@ export default {
},
isChanged() {
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-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;
.comment-icon {
@ -423,10 +449,12 @@ export default {
bottom: 60px;
}
/* todo: refactor */
.row-col {
& > div > input,
& > div div > input,
//& > div div > input,
& > div > .xc-input > input,
& > div > .xc-input >div > input,
& > div > select,
& > div > .xc-input > select,
& > div textarea {
@ -456,9 +484,10 @@ export default {
background: #363636;
.row-col {
& > div div > input,
//& > div div > input,
& > div > input,
& > div > .xc-input > input,
& > div > .xc-input >div > input,
& > div > select,
& > div > .xc-input > select,
& > div textarea {
@ -472,8 +501,9 @@ export default {
.row-col {
& > div > input,
& > div div > input,
//& > div div > input,
& > div > .xc-input > input,
& > div > .xc-input >div > input,
& > div > select,
& > div > .xc-input > select,
& > div textarea {

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

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

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

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

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

@ -83,6 +83,7 @@
v-model="selectedParent"
@cancel="selectedParent = null"
@input="onParentSave"
:breadcrumbs="breadcrumbs"
></component>
</v-dialog>
@ -101,6 +102,12 @@ export default {
name: "belongs-to-cell",
components: {ListChildItems, ItemChip, ListItems},
props: {
breadcrumbs: {
type: Array,
default() {
return [];
}
},
isForm: Boolean,
value: [Object, Array],
meta: [Object],
@ -283,7 +290,7 @@ export default {
const 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) {
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>
<v-spacer>
</v-spacer>
<v-icon small class="mr-1" @click="loadData()">mdi-reload</v-icon>
<v-btn
small
class="caption"

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

@ -14,6 +14,8 @@
class=" caption search-field ml-2"
/>
<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-icon small>mdi-plus</v-icon>&nbsp;
New Record
@ -37,8 +39,8 @@
<span class="grey--text caption primary-key "
v-if="primaryKey">(Primary Key : {{ ch[primaryKey] }})</span>
<v-spacer/>
<v-chip v-if="hm && ch[meta._tn]" x-small>
{{ ch[primaryCol] }}
<v-chip v-if="hm && ch[`${hm._rtn}Read`] && ch[`${hm._rtn}Read`][hmParentPrimaryValCol]" x-small>
{{ ch[`${hm._rtn}Read`][hmParentPrimaryValCol] }}
</v-chip>
</v-card-text>
</v-card>
@ -74,7 +76,7 @@ export default {
components: {Pagination},
props: {
value: Boolean,
hm: Boolean,
hm: [Object, Function],
title: {
type: String,
default: 'Link Record'
@ -91,7 +93,8 @@ export default {
size: Number,
api: [Object, Function],
mm: [Object, Function],
parentId: [String, Number]
parentId: [String, Number],
parentMeta:[Object]
},
data: () => ({
data: null,
@ -127,6 +130,11 @@ export default {
}, get() {
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
v-if="newRecordModal"
:hm="true"
:hm="hm"
:size="10"
:meta="childMeta"
:primary-col="childPrimaryCol"
:primary-key="childPrimaryKey"
v-model="newRecordModal"
:api="childApi"
:parent-meta="meta"
@add-new-record="insertAndAddNewChildRecord"
@add="addChildToParent"
:query-params="{
@ -103,6 +104,7 @@
ref="expandedForm"
:is-new.sync="isNewChild"
:disabled-columns="disabledChildColumns"
:breadcrumbs="breadcrumbs"
></component>
</v-dialog>
@ -132,6 +134,12 @@ export default {
listChildItemsModal
},
props: {
breadcrumbs: {
type: Array,
default() {
return [];
}
},
value: [Object, Array],
meta: [Object],
hm: Object,

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

@ -95,6 +95,7 @@
ref="expandedForm"
:is-new.sync="isNewChild"
v-model="selectedChild"
:breadcrumbs="breadcrumbs"
@cancel="selectedChild = null"
@input="onChildSave"
></component>
@ -119,6 +120,12 @@ export default {
name: "many-to-many-cell",
components: {ListChildItems, ItemChip, ListItems, DlgLabelSubmitCancel, listChildItemsModal},
props: {
breadcrumbs: {
type: Array,
default() {
return [];
}
},
value: [Object, Array],
meta: [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.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>
</template>
@ -71,6 +71,7 @@
:edit-column="true"
:column="column"
:meta="meta"
v-on="$listeners"
></edit-virtual-column>
</v-menu>
</div>
@ -149,7 +150,6 @@ export default {
<style scoped>
.name {
max-width: calc(100px - 40px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

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

@ -68,7 +68,6 @@
Save
</x-btn>
<fields
v-model="showFields"
:field-list="fieldList"
@ -136,8 +135,6 @@
</v-icon>
</x-btn>
</v-toolbar>
<div :class="`cell-height-${cellHeight}`"
style=" height:calc(100% - 32px);overflow:auto;transition: width 100ms "
class="d-flex"
@ -175,6 +172,7 @@
@expandRow="expandRow"
@onRelationDelete="loadMeta"
@loadTableData="loadTableData"
@loadMeta="loadMeta"
></xc-grid-view>
</template>
<template v-else-if="selectedView && selectedView.show_as === 'gallery' ">
@ -728,7 +726,22 @@ export default {
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, {
row: insertedData,
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'"
v-xc-ver-resize
v-for="(col,i) in availableColumns"
:key="i + '_' + col._cn"
:key=" col._cn"
v-show="showFields[col._cn]"
@xcresize="onresize(col._cn,$event)"
@xcresizing="onXcResizing(col._cn,$event)"
@ -113,7 +113,7 @@
<td
class="cell pointer"
v-for="(columnObj,col) in availableColumns"
:key="row + '_' + col + columnObj._cn"
:key="row + columnObj._cn"
:class="{
'active' : !isPublicView && selected.col === col && selected.row === row && isEditable ,
'primary-column' : primaryValueColumn === columnObj._cn,
@ -126,7 +126,7 @@
:data-col="columnObj._cn"
>
<virtual-cell
v-if="columnObj.virtual "
v-if="columnObj.virtual"
:column="columnObj"
:row="rowObj"
:nodes="nodes"
@ -139,15 +139,9 @@
@updateCol="(...args) => updateCol(...args, columnObj.bt && meta.columns.find( c => c.cn === columnObj.bt.cn), col, row)"
></virtual-cell>
<!--
<span
v-if="columnObj.virtual "
></span>
-->
<editable-cell
v-else-if="
!isLocked
!isLocked
&& !isPublicView
&& (editEnabled.col === col && editEnabled.row === row)
|| enableEditable(columnObj)

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

@ -122,7 +122,7 @@
v-ge="['columns','save-and-scaffold']">
<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
</v-list-item-title>
@ -132,7 +132,7 @@
v-ge="['columns','save-and-scaffold']"
>
<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
</v-list-item-title>
@ -144,7 +144,7 @@
v-ge="['columns','scaffold']"
>
<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
</v-list-item-title>
@ -154,7 +154,7 @@
v-ge="['columns','scaffold']"
>
<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
</v-list-item-title>
@ -164,7 +164,7 @@
v-ge="['columns','scaffold']"
>
<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
</v-list-item-title>
@ -174,7 +174,7 @@
v-ge="['columns','scaffold']"
>
<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
</v-list-item-title>
@ -191,7 +191,7 @@
v-ge="['columns','save-and-scaffold']"
@click="saveAndScaffold()">
<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
</v-list-item-title>
@ -201,7 +201,7 @@
v-ge="['columns','save-and-scaffold']"
@click="saveAndScaffoldModel()">
<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
</v-list-item-title>
@ -211,7 +211,7 @@
v-ge="['columns','json-to-column']"
@click="scaffold({model:true})">
<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
</v-list-item-title>
@ -221,7 +221,7 @@
@click="scaffold({router:true})"
>
<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
</v-list-item-title>
@ -231,7 +231,7 @@
@click="scaffold({service:true})"
>
<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
</v-list-item-title>
@ -241,7 +241,7 @@
@click="scaffold({relations:true})"
>
<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
</v-list-item-title>
@ -255,7 +255,7 @@
@click="showJsonToColumDlg = true"
>
<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;
JSON To Columns
</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>
<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>
</v-card-text>
@ -224,7 +224,7 @@ export default {
info: null,
aggregatedInfo: null,
apiTypeIcon: {
'rest': {icon: 'mdi-json', iconColor: 'green'},
'rest': {icon: 'mdi-code-json', iconColor: 'green'},
'graphql': {icon: 'mdi-graphql', iconColor: 'pink'},
'grpc': {src: 'grpc-icon-color.png', type: 'img'},
},

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

@ -76,7 +76,7 @@ export default {
loading: false,
projectType: 'rest',
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 gRPC APIs on database',

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

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

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

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

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

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

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

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

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

@ -590,7 +590,7 @@ export default {
{text: "Disabled", value: "none"},
],
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 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,
projectType: 'rest',
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 gRPC APIs on database',

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

@ -112,7 +112,7 @@ export default {
loading: false,
projectType: 'rest',
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: 'Automatic gRPC APIs on database',

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

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

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

@ -391,12 +391,15 @@ export const actions = {
// clear meta cache on relation create/delete
// todo: clear only necessary metas
// todo: include missing operations
if (['relationCreate',
if ([
'xcModelSet',
'relationCreate',
'xcM2MRelationCreate',
'xcVirtualRelationCreate',
'relationDelete',
'xcVirtualRelationDelete',
'xcRelationColumnDelete'].includes(op)) {
'xcRelationColumnDelete'
].includes(op)) {
commit('meta/MutClear', null, {root: true})
}

42
packages/nocodb/package-lock.json generated

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

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

@ -224,6 +224,10 @@ export default class NcProjectBuilder {
await curBuilder.onValidationUpdate(data.req.args.tn);
console.log(`Updated validations for table : ${data.req.args.tn}`)
break;
case 'xcUpdateVirtualKeyAlias':
await curBuilder.onVirtualColumnAliasUpdate(data.req.args.tn);
console.log(`Updated validations for table : ${data.req.args.tn}`)
break;
case 'xcModelSchemaSet':
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);
// 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)
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 || [];
if (tableMetaA.manyToMany.every(mm => mm.vtn === meta.vtn)) {
if (tableMetaA.manyToMany.every(mm => mm.vtn !== meta.vtn)) {
tableMetaA.manyToMany.push({
"tn": tableMetaA.tn,
"cn": meta.belongsTo[0].rcn,
@ -1232,7 +1234,7 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
metas.add(tableMetaA)
}
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({
"tn": tableMetaB.tn,
"cn": meta.belongsTo[1].rcn,
@ -1719,6 +1721,12 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
await this.getManyToManyRelations({localMetas: 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};

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> {
this.log(`onTableCreate : '%s' `, tn)
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();
}
// todo: m2m
public async onTableRename(oldTableName: string, newTableName: string): Promise<void> {
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];
}
public async list(args, {req, res}): Promise<any> {
const startTime = process.hrtime();
public async list(args, {req, res}: { req: any & { model: BaseModelSql }, res: any }): Promise<any> {
const startTime = process.hrtime();
try {
if (args.conditionGraph && typeof args.conditionGraph === 'string') {
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);
break;
case 'xcUpdateVirtualKeyAlias':
result = await this.xcUpdateVirtualKeyAlias(args);
break;
case 'xcRelationsGet':
result = await this.xcRelationsGet(args);
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
protected async xcRelationsGet(args): Promise<any> {
const dbAlias = await this.getDbAlias(args);
@ -2461,120 +2501,73 @@ export default class NcMetaMgr {
protected async xcRelationColumnDelete(args: any, req, deleteColumn = true): Promise<any> {
// this.xcMeta.startTransaction();
// try {
const dbAlias = this.getDbAlias(args);
const projectId = this.getProjectId(args);
const dbAlias = this.getDbAlias(args);
const projectId = this.getProjectId(args);
// const parent = await this.xcMeta.metaGet(projectId, dbAlias, 'nc_models', {
// title: args.args.parentTable
// });
// // @ts-ignore
// const parentMeta = JSON.parse(parent.meta);
// @ts-ignore
// todo: compare column
switch (args.args.type) {
case 'bt':
case 'hm':
const child = await this.xcMeta.metaGet(projectId, dbAlias, 'nc_models', {
title: args.args.childTable
});
const childMeta = JSON.parse(child.meta);
const relation = childMeta.belongsTo.find(bt => bt.rtn === args.args.parentTable);
// todo: virtual relation delete
if (relation) {
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({
// const parent = await this.xcMeta.metaGet(projectId, dbAlias, 'nc_models', {
// title: args.args.parentTable
// });
// // @ts-ignore
// const parentMeta = JSON.parse(parent.meta);
// @ts-ignore
// todo: compare column
switch (args.args.type) {
case 'bt':
case 'hm':
const child = await this.xcMeta.metaGet(projectId, dbAlias, 'nc_models', {
title: args.args.childTable
});
const childMeta = JSON.parse(child.meta);
const relation = childMeta.belongsTo.find(bt => bt.rtn === args.args.parentTable);
// todo: virtual relation delete
if (relation) {
const opArgs = {
...args,
args: {
parentTable: rel2.rtn,
parentColumn: rel2.rcn,
childTable: rel2.tn,
childColumn: rel2.cn,
type: 'bt',
}
}, req, false);
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: assocMeta,
api: 'tableDelete',
args: {
columns,
originalColumns,
tn: childMeta.tn,
},
sqlOpPlus: true,
};
const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableDelete', opArgs);
api: 'tableUpdate'
}
const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableUpdate', opArgs);
if (this.listener) {
await this.listener({
@ -2584,10 +2577,57 @@ export default class NcMetaMgr {
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()
// } catch (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;
case "set":
return "[String]";
return "JSON";
break;
case "geometry":

Loading…
Cancel
Save