Browse Source

feat: Lookup - update meta data structure, Add icon

Signed-off-by: Pranav C <61551451+pranavxc@users.noreply.github.com>
pull/401/head
Pranav C 3 years ago
parent
commit
93da24c163
  1. 4
      packages/nc-gui/components/project/spreadsheet/components/editColumn.vue
  2. 79
      packages/nc-gui/components/project/spreadsheet/components/editColumn/lookupOptions.vue
  3. 130
      packages/nc-gui/components/project/spreadsheet/components/expandedForm.vue
  4. 2
      packages/nc-gui/components/project/spreadsheet/components/virtualCell.vue
  5. 44
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/lookupCell.vue
  6. 24
      packages/nc-gui/components/project/spreadsheet/components/virtualHeaderCell.vue
  7. 2
      packages/nc-gui/components/project/spreadsheet/helpers/uiTypes.js
  8. 20
      packages/nc-gui/components/project/spreadsheet/mixins/spreadsheet.js
  9. 9
      packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue
  10. 91
      packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue
  11. 6
      packages/nc-gui/plugins/globalMixin.js
  12. 2
      packages/nc-gui/plugins/ncApis/index.js
  13. 8
      packages/nocodb/package-lock.json
  14. 2
      packages/nocodb/package.json
  15. 28
      packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts
  16. 10
      packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts
  17. 13
      packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts

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

@ -20,6 +20,7 @@
</v-col>
<v-col cols="12">
<v-text-field
v-if="!isLookup"
ref="column"
v-model="newColumn.cn"
hide-details="auto"
@ -32,7 +33,6 @@
label="Column name"
dense
outlined
:disabled="isLookup"
@input="newColumn.altered = newColumn.altered || 8"
/>
</v-col>
@ -485,7 +485,7 @@ export default {
return this.$emit('saved')
}
if (this.isLookup && this.$refs.lookup) {
await this.$refs.lookup.save()
return await this.$refs.lookup.save()
}
this.newColumn.tn = this.nodes.tn

79
packages/nc-gui/components/project/spreadsheet/components/editColumn/lookupOptions.vue

@ -12,14 +12,14 @@
label="Child Table"
:full-width="false"
:items="refTables"
item-text="_tn"
item-text="_ltn"
:item-value="v => v"
:rules="[v => !!v || 'Required']"
dense
>
<template #item="{item}">
<span class="caption"><span class="font-weight-bold"> {{
item._tn
item._ltn
}}</span> <small>({{ relationNames[item.type] }})
</small></span>
</template>
@ -35,7 +35,7 @@
label="Child column"
:full-width="false"
:items="columnList"
item-text="_cn"
item-text="_lcn"
dense
:loading="loadingColumns"
:item-value="v => v"
@ -65,9 +65,46 @@ export default {
refTables() {
return this.meta
? [
...(this.meta.belongsTo || []).map(bt => ({ type: 'bt', relation: bt, tn: bt.rtn, _tn: bt._rtn })),
...(this.meta.hasMany || []).map(hm => ({ type: 'hm', relation: hm, tn: hm.tn, _tn: hm._tn })),
...(this.meta.manyToMany || []).map(mm => ({ type: 'mm', relation: mm, tn: mm.rtn, _tn: mm._rtn }))
...(this.meta.belongsTo || []).map(({ rtn, _rtn, rcn, tn, cn }) => ({
type: 'bt',
rtn,
_rtn,
rcn,
tn,
cn,
ltn: rtn,
_ltn: _rtn
})),
...(this.meta.hasMany || []).map(({
tn,
_tn,
cn,
rcn,
rtn
}) => ({
type: 'hm',
tn,
_tn,
cn,
rcn,
rtn,
ltn: tn,
_ltn: _tn
})),
...(this.meta.manyToMany || []).map(({ vtn, _vtn, vrcn, vcn, rtn, _rtn, rcn, tn, cn }) => ({
type: 'mm',
tn,
cn,
vtn,
_vtn,
vrcn,
rcn,
rtn,
vcn,
_rtn,
ltn: rtn,
_ltn: _rtn
}))
]
: []
},
@ -76,18 +113,21 @@ export default {
this.lookup &&
this.lookup.table &&
this.$store.state.meta.metas &&
this.$store.state.meta.metas[this.lookup.table.tn] &&
this.$store.state.meta.metas[this.lookup.table.tn].columns
) || []).map(({ cn, _cn }) => ({ cn, _cn }))
this.$store.state.meta.metas[this.lookup.table.ltn] &&
this.$store.state.meta.metas[this.lookup.table.ltn].columns
) || []).map(({ cn, _cn }) => ({
lcn: cn,
_lcn: _cn
}))
}
},
methods: {
checkLookupExist(v) {
return (this.lookup.table && (this.meta.v || []).every(c => !(
c.lookup &&
c.type === this.lookup.table.type &&
c.tn === this.lookup.table.tn &&
c.cn === v.cn
c.lk &&
c.lk.type === this.lookup.table.type &&
c.lk.ltn === this.lookup.table.ltn &&
c.lk.lcn === v.lcn
))) || 'Lookup already exist'
},
async onTableChange() {
@ -97,7 +137,7 @@ export default {
await this.$store.dispatch('meta/ActLoadMeta', {
dbAlias: this.nodes.dbAlias,
env: this.nodes.env,
tn: this.lookup.table.tn
tn: this.lookup.table.ltn
})
} catch (e) {
// ignore
@ -117,10 +157,11 @@ export default {
const meta = JSON.parse(JSON.stringify(this.$store.state.meta.metas[this.meta.tn]))
meta.v.push({
// _cn: this.alias,
lookup: true,
...this.lookup.table,
...this.lookup.column
_cn: this.alias,
lk: {
...this.lookup.table,
...this.lookup.column
}
})
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
@ -131,7 +172,7 @@ export default {
meta
}])
return this.$emit('saved', `${this.lookup.column._cn} (from ${this.lookup.table._tn})`)
return this.$emit('saved', `${this.lookup.column._lcn} (from ${this.lookup.table._ltn})`)
} catch (e) {
this.$toast.error(e.message).goAway(3000)
}

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

@ -59,82 +59,86 @@
<v-container fluid style="height:70vh" class="py-0">
<v-row class="h-100">
<v-col class="h-100 px-10" style="overflow-y: auto" cols="8" :offset="isNew || !toggleDrawer ? 2 : 0">
<div
<template
v-for="(col,i) in fields"
:key="i"
:class="{
'active-row' : active === col._cn,
required: isRequired(col, localState)
}"
class="row-col my-4"
>
<div>
<label :for="`data-table-form-${col._cn}`" class="body-2 text-capitalize">
<virtual-header-cell
<div
v-if="!col.lk"
:key="i"
:class="{
'active-row' : active === col._cn,
required: isRequired(col, localState)
}"
class="row-col my-4"
>
<div>
<label :for="`data-table-form-${col._cn}`" class="body-2 text-capitalize">
<virtual-header-cell
v-if="col.virtual"
:column="col"
:nodes="nodes"
:is-form="true"
:meta="meta"
/>
<header-cell
v-else
:is-form="true"
:is-foreign-key="col.cn in belongsTo || col.cn in hasMany"
:value="col._cn"
:column="col"
:sql-ui="sqlUi"
/>
</label>
<virtual-cell
v-if="col.virtual"
ref="virtual"
:disabled-columns="disabledColumns"
:column="col"
:row="localState"
:nodes="nodes"
:is-form="true"
:meta="meta"
:api="api"
:active="true"
:sql-ui="sqlUi"
:is-new="isNew"
:is-form="true"
:breadcrumbs="localBreadcrumbs"
@updateCol="updateCol"
@newRecordsSaved="$listeners.loadTableData|| reload"
/>
<header-cell
<div
v-else-if="col.ai || (col.pk && !isNew) || disabledColumns[col._cn]"
style="height:100%; width:100%"
class="caption xc-input"
@click="col.ai && $toast.info('Auto Increment field is not editable').goAway(3000)"
>
<input
style="height:100%; width: 100%"
readonly
disabled
:value="localState[col._cn]"
>
</div>
<editable-cell
v-else
:is-form="true"
:is-foreign-key="col.cn in belongsTo || col.cn in hasMany"
:value="col._cn"
:id="`data-table-form-${col._cn}`"
v-model="localState[col._cn]"
:db-alias="dbAlias"
:column="col"
class="xc-input body-2"
:meta="meta"
:sql-ui="sqlUi"
is-form
@focus="active = col._cn"
@blur="active = ''"
@input="$set(changedColumns,col._cn, true)"
/>
</label>
<virtual-cell
v-if="col.virtual"
ref="virtual"
:disabled-columns="disabledColumns"
:column="col"
:row="localState"
:nodes="nodes"
:meta="meta"
:api="api"
:active="true"
:sql-ui="sqlUi"
:is-new="isNew"
:is-form="true"
:breadcrumbs="localBreadcrumbs"
@updateCol="updateCol"
@newRecordsSaved="$listeners.loadTableData|| reload"
/>
<div
v-else-if="col.ai || (col.pk && !isNew) || disabledColumns[col._cn]"
style="height:100%; width:100%"
class="caption xc-input"
@click="col.ai && $toast.info('Auto Increment field is not editable').goAway(3000)"
>
<input
style="height:100%; width: 100%"
readonly
disabled
:value="localState[col._cn]"
>
</div>
<editable-cell
v-else
:id="`data-table-form-${col._cn}`"
v-model="localState[col._cn]"
:db-alias="dbAlias"
:column="col"
class="xc-input body-2"
:meta="meta"
:sql-ui="sqlUi"
is-form
@focus="active = col._cn"
@blur="active = ''"
@input="$set(changedColumns,col._cn, true)"
/>
</div>
</div>
</template>
</v-col>
<v-col
v-if="!isNew && toggleDrawer"

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

@ -118,7 +118,7 @@ export default {
return this.column && this.column.mm
},
lookup() {
return this.column && this.column.lookup
return this.column && this.column.lk
}
},
methods: {

44
packages/nc-gui/components/project/spreadsheet/components/virtualCell/lookupCell.vue

@ -63,42 +63,32 @@ export default {
return this.column && this.$ncApis.get({
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
table: this.column.tn
table: this.column.lk.ltn
})
// return this.lookUpMeta && this.lookUpMeta._tn
// ? ApiFactory.create(
// this.$store.getters['project/GtrProjectType'],
// this.lookUpMeta._tn,
// this.lookUpMeta.columns,
// this,
// this.lookUpMeta
// )
// : null
},
lookUpMeta() {
return this.$store.state.meta.metas[this.column.tn]
return this.$store.state.meta.metas[this.column.lk.ltn]
},
assocMeta() {
return this.column.type === 'mm' && this.$store.state.meta.metas[this.column.relation.vtn]
return this.column.lk.type === 'mm' && this.$store.state.meta.metas[this.column.lk.vtn]
},
lookUpColumnAlias() {
if (!this.lookUpMeta || !this.column.cn) {
if (!this.lookUpMeta || !this.column.lk.lcn) {
return
}
return (this.$store.state.meta.metas[this.column.tn].columns.find(cl => cl.cn === this.column.cn) || {})._cn
return (this.$store.state.meta.metas[this.column.lk.ltn].columns.find(cl => cl.cn === this.column.lk.lcn) || {})._cn
},
localValueObj() {
if (!this.column || !this.row) {
return null
}
switch (this.column.type) {
switch (this.column.lk.type) {
case 'mm':
return this.row[`${this.column._tn}MMList`]
return this.row[`${this.column.lk._ltn}MMList`]
case 'hm':
return this.row[`${this.column._tn}List`]
return this.row[`${this.column.lk._ltn}List`]
case 'bt':
return this.row[`${this.column._tn}Read`]
return this.row[`${this.column.lk._ltn}Read`]
default:
return null
}
@ -113,19 +103,19 @@ export default {
return [this.localValueObj[this.lookUpColumnAlias]]
},
queryParams() {
switch (this.column.type) {
switch (this.column.lk.type) {
case 'bt':
return { where: `(${this.lookUpMeta.columns.find(c => c.cn === this.column.relation.rcn)._cn},eq,${this.row[this.meta.columns.find(c => c.cn === this.column.relation.cn)._cn]})` }
return { where: `(${this.lookUpMeta.columns.find(c => c.cn === this.column.lk.rcn)._cn},eq,${this.row[this.meta.columns.find(c => c.cn === this.column.lk.cn)._cn]})` }
case 'hm':
return { where: `(${this.lookUpMeta.columns.find(c => c.cn === this.column.relation.cn)._cn},eq,${this.row[this.meta.columns.find(c => c.cn === this.column.relation.rcn)._cn]})` }
return { where: `(${this.lookUpMeta.columns.find(c => c.cn === this.column.lk.cn)._cn},eq,${this.row[this.meta.columns.find(c => c.cn === this.column.lk.rcn)._cn]})` }
case 'mm':
return this.assocMeta
? {
conditionGraph: {
[this.assocMeta.tn]: {
relationType: 'hm',
[this.assocMeta.columns.find(c => c.cn === this.column.relation.vcn).cn]: {
eq: this.row[this.meta.columns.find(c => c.cn === this.column.relation.cn)._cn]
[this.assocMeta.columns.find(c => c.cn === this.column.lk.vcn).cn]: {
eq: this.row[this.meta.columns.find(c => c.cn === this.column.lk.cn)._cn]
}
}
}
@ -145,14 +135,14 @@ export default {
await this.$store.dispatch('meta/ActLoadMeta', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.column.tn
tn: this.column.lk.ltn
})
}
if (this.column.type === 'mm' && !this.assocMeta) {
if (this.column.lk.type === 'mm' && !this.assocMeta) {
await this.$store.dispatch('meta/ActLoadMeta', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.column.relation.vtn
tn: this.column.lk.vtn
})
}
},

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

@ -11,9 +11,17 @@
<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.lookup" color="pink" x-small class="mr-1" v-on="on">
mdi-table-network
</v-icon>
<template v-else-if="column.lk">
<v-icon v-if="column.lk.type === 'hm'" color="warning" x-small class="mr-1" v-on="on">
mdi-table-column-plus-before
</v-icon>
<v-icon v-else-if="column.lk.type === 'bt'" color="info" x-small class="mr-1" v-on="on">
mdi-table-column-plus-before
</v-icon>
<v-icon v-else-if="column.lk.type === 'mm'" color="pink" x-small class="mr-1" v-on="on">
mdi-table-column-plus-before
</v-icon>
</template>
<span class="name flex-grow-1" :title="column._cn" v-on="on" v-html="alias">
@ -31,7 +39,7 @@
</v-icon>
</template>
<v-list dense>
<v-list-item v-if="!column.lookup" dense @click="editColumnMenu = true">
<v-list-item v-if="!column.lk" dense @click="editColumnMenu = true">
<x-icon small class="mr-1" color="primary">
mdi-pencil
</x-icon>
@ -112,7 +120,7 @@ export default {
}),
computed: {
alias() {
return this.column.lookup ? `${this.column._cn} <small class="grey--text text--darken-1">(from ${this.column._tn})</small>` : this.column._cn
return this.column.lk ? `${this.column.lk._lcn} <small class="grey--text text--darken-1">(from ${this.column.lk._ltn})</small>` : this.column._cn
},
type() {
if (this.column.bt) {
@ -184,8 +192,8 @@ export default {
return `'${this.column.mm._tn}' & '${this.column.mm._rtn}' have <br>many to many relation`
} else if (this.column.bt) {
return `'${this.column.bt._tn}' belongs to '${this.column.bt._rtn}'`
} else if (this.column.lookup) {
return `'${this.column._cn}' from '${this.column._tn}' (${this.column.type}))`
} else if (this.column.lk) {
return `'${this.column.lk._lcn}' from '${this.column.lk._ltn}' (${this.column.lk.type})`
}
return ''
}
@ -240,7 +248,7 @@ export default {
}
},
async deleteColumn() {
if (this.column.lookup) {
if (this.column.lk) {
await this.deleteLookupColumn()
} else {
await this.deleteRelation()

2
packages/nc-gui/components/project/spreadsheet/helpers/uiTypes.js

@ -14,7 +14,7 @@ const uiTypes = [
},
{
name: 'Lookup',
icon: 'mdi-link-variant'
icon: 'mdi-table-column-plus-before'
},
{
name: 'SingleLineText',

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

@ -98,8 +98,8 @@ export default {
{
const _ref = {}
columns.forEach((c) => {
if (c.virtual && c.lookup) {
c.alias = `${c._cn} (from ${c._tn})`
if (c.virtual && c.lk) {
c.alias = `${c.lk._lcn} (from ${c.lk._ltn})`
} else {
c.alias = c._cn
}
@ -152,7 +152,7 @@ export default {
// todo: handle if virtual column missing
// construct fields args based on lookup columns
const fieldsObj = ((this.meta && this.meta.v && this.meta.v) || []).reduce((obj, vc) => {
if (!vc.lookup) {
if (!vc.lk) {
return obj
}
@ -160,21 +160,21 @@ export default {
let index
let column
switch (vc.type) {
switch (vc.lk.type) {
case 'mm':
index = nestedFields.mm.indexOf(vc.tn) + 1
index = nestedFields.mm.indexOf(vc.lk.ltn) + 1
key = `mfields${index}`
column = vc.cn
column = vc.lk.lcn
break
case 'hm':
index = nestedFields.hm.indexOf(vc.tn) + 1
index = nestedFields.hm.indexOf(vc.lk.ltn) + 1
key = `hfields${index}`
column = vc.cn
column = vc.lk.lcn
break
case 'bt':
index = nestedFields.bt.indexOf(vc.tn) + 1
index = nestedFields.bt.indexOf(vc.lk.ltn) + 1
key = `bfields${index}`
column = vc.cn
column = vc.lk.lcn
break
}

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

@ -184,7 +184,7 @@
>
<div class="flex-grow-1 h-100" style="overflow-y: auto">
<div ref="table" style="height : calc(100% - 36px); overflow: auto;width:100%">
<v-skeleton-loader v-if="!dataLoaded && (loadingData || loadingMeta)" type="table" />
<v-skeleton-loader v-if="!dataLoaded && (loadingData || loadingData)" type="table" />
<template v-else-if="selectedView && (selectedView.type === 'table' || selectedView.show_as === 'grid' )">
<xc-grid-view
:key="key"
@ -601,7 +601,9 @@ export default {
async mounted() {
try {
await this.createTableIfNewTable()
this.loadingMeta = true
await this.loadMeta()
this.loadingMeta = false
if (this.relationType === 'hm') {
this.filters.push({
@ -935,10 +937,13 @@ export default {
if (updateShowFields) {
try {
const qp = JSON.parse(tableMeta.query_params)
this.showFields = qp.showFields ? qp.showFields : this.showFields
this.showFields = qp.showFields || this.showFields
if (col) {
this.$set(this.showFields, col, true)
}
if (this.selectedViewId === tableMeta.id) {
this.columnsWidth = qp.columnsWidth || this.columnsWidth
}
} catch (e) {
}
}

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

@ -27,7 +27,7 @@
@xcresized="resizingCol = null"
>
<!-- :style="columnsWidth[col._cn] ? `min-width:${columnsWidth[col._cn]}; max-width:${columnsWidth[col._cn]}` : ''"
-->
-->
<virtual-header-cell
v-if="col.virtual"
@ -208,6 +208,7 @@
</table>
<div is="style" v-html="style" />
<div is="style" v-html="resizeColStyle" />
</div>
</template>
@ -290,27 +291,17 @@ export default {
},
style() {
let style = ''
for (const [key, val] of Object.entries(this.columnsWidth || {})) {
if (val && key !== this.resizingCol) {
style += `
[data-col="${key}"]{
min-width: ${val};
max-width: ${val};
width: ${val};
}
`
} else if (key === this.resizingCol) {
style += `
[data-col="${key}"]{
min-width: ${this.resizingColWidth};
max-width: ${this.resizingColWidth};
width: ${this.resizingColWidth};
}
`
for (const c of this.availableColumns) {
const val = (this.columnsWidth && this.columnsWidth[c.alias]) || (c.virtual ? '200px' : (columnStyling[c.uidt] && columnStyling[c.uidt].w))
if (val && c.key !== this.resizingCol) {
style += `[data-col="${c.alias}"]{min-width:${val};max-width:${val};width: ${val};}`
}
}
return style
},
resizeColStyle() {
return this.resizingCol ? ` [data-col="${this.resizingCol}"]{min-width:${this.resizingColWidth};max-width:${this.resizingColWidth};width:${this.resizingColWidth};}` : ''
}
},
watch: {
@ -344,20 +335,20 @@ export default {
this.onCellValueChange(colIndex, rowIndex, columnObj)
},
calculateColumnWidth() {
setTimeout(() => {
const obj = {}
this.meta && this.meta.columns && this.meta.columns.forEach((c) => {
obj[c._cn] = (columnStyling[c.uidt] && columnStyling[c.uidt].w) || undefined
})
this.meta && this.meta.v && this.meta.v.forEach((v) => {
obj[v._cn] = v.bt ? '100px' : '200px'
})
Array.from(this.$el.querySelectorAll('th')).forEach((el) => {
const width = el.getBoundingClientRect().width
obj[el.dataset.col] = obj[el.dataset.col] || ((width < 100 ? 100 : width) + 'px')
})
this.$emit('update:columnsWidth', { ...obj, ...(this.columnWidth || {}) })
}, 500)
// setTimeout(() => {
// const obj = {}
// this.meta && this.meta.columns && this.meta.columns.forEach((c) => {
// obj[c._cn] = (columnStyling[c.uidt] && columnStyling[c.uidt].w) || undefined
// })
// this.meta && this.meta.v && this.meta.v.forEach((v) => {
// obj[v._cn] = v.bt ? '100px' : '200px'
// })
// Array.from(this.$el.querySelectorAll('th')).forEach((el) => {
// const width = el.getBoundingClientRect().width
// obj[el.dataset.col] = obj[el.dataset.col] || ((width < 100 ? 100 : width) + 'px')
// })
// this.$emit('update:columnsWidth', { ...obj, ...(this.columnWidth || {}) })
// }, 2000)
},
isCentrallyAligned(col) {
return !['SingleLineText',
@ -372,7 +363,9 @@ export default {
'LastModifiedTime'].includes(col.uidt)
},
async xcAuditModelCommentsCount() {
if (this.isPublicView || !this.data || !this.data.length) { return }
if (this.isPublicView || !this.data || !this.data.length) {
return
}
const aggCount = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
dbAlias: this.nodes.dbAlias
}, 'xcAuditModelCommentsCount', {
@ -386,23 +379,33 @@ export default {
},
onKeyDown(e) {
if (this.selected.col === null || this.selected.row === null) { return }
if (this.selected.col === null || this.selected.row === null) {
return
}
switch (e.keyCode) {
// left
case 37:
if (this.selected.col > 0) { this.selected.col-- }
if (this.selected.col > 0) {
this.selected.col--
}
break
// right
case 39:
if (this.selected.col < this.colLength - 1) { this.selected.col++ }
if (this.selected.col < this.colLength - 1) {
this.selected.col++
}
break
// up
case 38:
if (this.selected.row > 0) { this.selected.row-- }
if (this.selected.row > 0) {
this.selected.row--
}
break
// down
case 40:
if (this.selected.row < this.rowLength - 1) { this.selected.row++ }
if (this.selected.row < this.rowLength - 1) {
this.selected.row++
}
break
// enter
case 13:
@ -424,7 +427,9 @@ export default {
this.meta.columns &&
this.meta.columns[this.selected.col] &&
this.meta.columns[this.selected.col].virtual
) { return }
) {
return
}
this.selected.col = null
this.selected.row = null
},
@ -452,7 +457,9 @@ export default {
}
},
makeEditable(col, row) {
if (this.isPublicView || !this.isEditable) { return }
if (this.isPublicView || !this.isEditable) {
return
}
if (this.availableColumns[col].ai) {
return this.$toast.info('Auto Increment field is not editable').goAway(3000)
}
@ -777,8 +784,4 @@ th:first-child, td:first-child {
transform: rotate(90deg);
}
th {
min-width: 100px;
}
</style>

6
packages/nc-gui/plugins/globalMixin.js

@ -48,8 +48,6 @@ export default async({ store }) => {
el.appendChild(resizer)
resizer.addEventListener('mousedown', initDrag, false)
el.style.transition = '20ms width0'
// eslint-disable-next-line no-unused-vars
let startX, startY, startWidth
@ -76,10 +74,8 @@ export default async({ store }) => {
function doDrag(e) {
width = (startWidth + e.clientX - startX) + 'px'
el.style.maxWidth = el.style.minWidth = el.style.width = width
el.style.width = width
emit(vnode, 'xcresizing', width)
//
// p.style.height = (startHeight + e.clientY - startY) + 'px';
}
function stopDrag(e) {

2
packages/nc-gui/plugins/ncApis/index.js

@ -5,7 +5,7 @@ export default function({ store: $store, $axios, ...rest }, inject) {
let projectId = null
inject('ncApis', {
get: ({ table, dbAlias, env }) => {
get: ({ table, dbAlias = 'db', env = 'dev' }) => {
if (!$store.state.meta.metas[table]) {
return
}

8
packages/nocodb/package-lock.json generated

@ -1,6 +1,6 @@
{
"name": "nocodb",
"version": "0.11.1",
"version": "0.11.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -11800,9 +11800,9 @@
"integrity": "sha512-3AryS9uwa5NfISLxMciUonrH7YfXp+nlahB9T7girXIsLQrmwX4MdnuKs32akduCOGpKmjTJSWmATULbuMkbfw=="
},
"nc-help": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/nc-help/-/nc-help-0.2.7.tgz",
"integrity": "sha512-31wxoJmfZHJ4aP/YiI+GgZRqwulyB6Z5BamcCUw/NDdNVoze+zHX0CtmoxfSlyi/U4zUOG9rhennKPv6QmmsMQ==",
"version": "0.2.8",
"resolved": "https://registry.npmjs.org/nc-help/-/nc-help-0.2.8.tgz",
"integrity": "sha512-h6HUWb2trYEVm7x2/hWxifeq+ZALCIFxZFISgpI/IQPhe/BSoGUNpcRIhWg8wt1y87kxZ7/VCkvTQUhW9OTOGg==",
"requires": {
"axios": "^0.21.1",
"boxen": "^4.2.0",

2
packages/nocodb/package.json

@ -144,7 +144,7 @@
"mysql2": "^2.2.5",
"nanoid": "^3.1.20",
"nc-common": "0.0.6",
"nc-help": "^0.2.7",
"nc-help": "^0.2.8",
"nc-lib-gui": "^0.2.9",
"nc-plugin": "^0.1.1",
"nodemailer": "^6.4.10",

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

@ -474,10 +474,10 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
// update lookup columns
this.metas[bt.rtn].v?.forEach(v => {
if (v.tn === tn && v.cn === column.cno) {
if (v.lk && v.lk.ltn === tn && v.lk.lcn === column.cno) {
relationTableMetas.add(this.metas[bt.rtn])
v.cn = column.cn;
v._cn = column._cn;
v.lk.lcn = column.cn;
v.lk._lcn = column._cn;
}
})
}
@ -503,10 +503,10 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
// update lookup columns
this.metas[hm.tn].v?.forEach(v => {
if (v.tn === tn && v.cn === column.cno) {
if (v.lk && v.lk.ltn === tn && v.lk.lcn === column.cno) {
relationTableMetas.add(this.metas[hm.tn])
v.cn = column.cn;
v._cn = column._cn;
v.lk.lcn = column.cn;
v.lk._lcn = column._cn;
}
})
@ -533,10 +533,10 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
// update lookup columns
this.metas[mm.rtn].v?.forEach(v => {
if (v.tn === tn && v.cn === column.cno) {
if (v.lk &&v.lk.ltn === tn && v.lk.lcn === column.cno) {
relationTableMetas.add(this.metas[mm.tn])
v.cn = column.cn;
v._cn = column._cn;
v.lk.lcn = column.cn;
v.lk._lcn = column._cn;
}
})
@ -598,7 +598,7 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
for (const bt of newMeta.belongsTo) {
// filter out lookup columns which maps to current col
this.metas[bt.rtn].v = this.metas[bt.rtn].v?.filter(v => {
if (v.lookup && v.tn === tn && v.cn === column.cn) {
if (v.lk && v.lk.ltn === tn && v.lk.lcn === column.cn) {
relationTableMetas.add(this.metas[bt.rtn])
return false;
}
@ -613,7 +613,7 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
for (const hm of newMeta.hasMany) {
// filter out lookup columns which maps to current col
this.metas[hm.tn].v = this.metas[hm.tn].v?.filter(v => {
if (v.lookup && v.tn === tn && v.cn === column.cn) {
if (v.lk && v.lk.ltn === tn && v.lk.lcn === column.cn) {
relationTableMetas.add(this.metas[hm.tn])
return false;
}
@ -627,7 +627,7 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
for (const mm of newMeta.manyToMany) {
// filter out lookup columns which maps to current col
this.metas[mm.rtn].v=this.metas[mm.rtn].v?.filter(v => {
if (v.tn === tn && v.rcn === column.cn) {
if (v.lk && v.lk.ltn === tn && v.lk.lcn === column.cn) {
relationTableMetas.add(this.metas[mm.tn])
return false;
}
@ -904,11 +904,11 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
// filter lookup and relation virtual columns
parentMeta.v = parentMeta.v.filter(({mm, ...rest}) => (!mm || !(mm.tn === parent && mm.rtn === child || mm.tn === child && mm.rtn === parent))
// check for lookup
&& !(rest.type === 'mm' && (rest.relation.tn === parent && rest.relation.rtn === child || rest.relation.tn === child && rest.relation.rtn === parent))
&& !(rest.lk && rest.lk.type === 'mm' && (rest.lk.tn === parent && rest.lk.rtn === child || rest.lk.tn === child && rest.lk.rtn === parent))
)
childMeta.v = childMeta.v.filter(({mm, ...rest}) => (!mm || !(mm.tn === parent && mm.rtn === child || mm.tn === child && mm.rtn === parent))
// check for lookup
&& !(rest.type === 'mm' && (rest.relation.tn === parent && rest.relation.rtn === child || rest.relation.tn === child && rest.relation.rtn === parent))
&& !(rest.lk && rest.lk.type === 'mm' && (rest.lk.tn === parent && rest.lk.rtn === child || rest.lk.tn === child && rest.lk.rtn === parent))
)
for (const meta of [parentMeta, childMeta]) {

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

@ -1380,8 +1380,8 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
const oldMeta = JSON.parse(existingModel.meta);
Object.assign(oldMeta, {
hasMany: meta.hasMany,
v: oldMeta.v.filter(({hm, lookup, relation, type}) => (!hm || hm.rtn !== tnp || hm.tn !== tnc) &&
!(lookup && relation && type === 'hm' && relation.rtn === tnp && relation.tn === tnc))
v: oldMeta.v.filter(({hm, lk}) => (!hm || hm.rtn !== tnp || hm.tn !== tnc) &&
!(lk && lk.type === 'hm' && lk.rtn === tnp && lk.tn === tnc))
});
// todo: backup schema
await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
@ -1430,8 +1430,8 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
const oldMeta = JSON.parse(existingModel.meta);
Object.assign(oldMeta, {
belongsTo: meta.belongsTo,
v: oldMeta.v.filter(({bt, lookup, relation, type}) => (!bt || bt.rtn !== tnp || bt.tn !== tnc) &&
!(lookup && relation && type === 'bt' && relation.rtn === tnp && relation.tn === tnc))
v: oldMeta.v.filter(({bt, lk}) => (!bt || bt.rtn !== tnp || bt.tn !== tnc) &&
!(lk && lk.type === 'bt' && lk.rtn === tnp && lk.tn === tnc))
});
await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
title: tnc,
@ -1963,3 +1963,5 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

13
packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts

@ -1,4 +1,3 @@
import fs from 'fs';
import path from 'path';
@ -34,7 +33,7 @@ const log = debug('nc:api:rest');
const NC_CUSTOM_ROUTE_KEY = '__xc_custom';
export class RestApiBuilder extends BaseApiBuilder<Noco> {
public readonly type='rest';
public readonly type = 'rest';
private controllers: { [key: string]: RestCtrlBelongsTo | RestCtrl | RestCtrlHasMany | RestCtrlCustom };
private procedureCtrl: RestCtrlProcedure;
private routers: { [key: string]: Router };
@ -602,7 +601,7 @@ export class RestApiBuilder extends BaseApiBuilder<Noco> {
public async onTableCreate(tn: string, args?: any): Promise<void> {
await super.onTableCreate(tn,args);
await super.onTableCreate(tn, args);
const columns = args.columns ? {
[tn]: args.columns?.map(({altered: _al, ...rest}) => rest)
@ -1215,8 +1214,8 @@ export class RestApiBuilder extends BaseApiBuilder<Noco> {
const oldMeta = JSON.parse(existingModel.meta);
Object.assign(oldMeta, {
hasMany: meta.hasMany,
v: oldMeta.v.filter(({hm, lookup, relation, type}) => (!hm || hm.rtn !== tnp || hm.tn !== tnc) &&
!(lookup && relation && type==='hm'&&relation.rtn === tnp && relation.tn === tnc ))
v: oldMeta.v.filter(({hm, lk}) => (!hm || hm.rtn !== tnp || hm.tn !== tnc) &&
!(lk && lk.type === 'hm' && lk.rtn === tnp && lk.tn === tnc))
});
// todo: delete from query_params
@ -1252,8 +1251,8 @@ export class RestApiBuilder extends BaseApiBuilder<Noco> {
Object.assign(oldMeta, {
belongsTo: meta.belongsTo,
v: oldMeta.v.filter(({bt, relation, lookup,type}) => (!bt || bt.rtn !== tnp || bt.tn !== tnc) &&
!(lookup && relation && type==='bt'&&relation.rtn === tnp && relation.tn === tnc ))
v: oldMeta.v.filter(({bt, lk}) => (!bt || bt.rtn !== tnp || bt.tn !== tnc) &&
!(lk && lk.type === 'bt' && lk.rtn === tnp && lk.tn === tnc))
});
// todo: delete from query_params
await this.xcMeta.metaUpdate(this.projectId,

Loading…
Cancel
Save