Browse Source

feat: impelement shared grid view(WIP)

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/655/head
Pranav C 3 years ago
parent
commit
da9da645a6
  1. 7
      packages/nc-gui/components/project/spreadsheet/components/spreadsheetNavDrawer.vue
  2. 4
      packages/nc-gui/components/project/spreadsheet/components/virtualCell.vue
  3. 3
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/belongsToCell.vue
  4. 25
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listChildItems.vue
  5. 10
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listChildItemsModal.vue
  6. 8
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue
  7. 15
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/lookupCell.vue
  8. 6
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue
  9. 112
      packages/nc-gui/components/project/spreadsheet/public/xcTable.vue
  10. 5
      packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue
  11. 6
      packages/nocodb/src/lib/dataMapper/lib/sql/CustomKnex.ts
  12. 119
      packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts
  13. 8
      packages/nocodb/src/lib/noco/meta/NcMetaMgrEE.ts

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

@ -353,10 +353,11 @@
<!-- <v-menu offset-x left>-->
<!-- <template v-slot:activator="{on}">-->
<!-- v-show="-->
<!-- selectedView && selectedView.show_as === 'form'-->
<!-- "-->
<v-list-item
v-show="
selectedView && selectedView.show_as === 'form'
"
v-if="_isUIAllowed('shareview')"
@click="genShareLink"
>

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

@ -18,6 +18,7 @@
:required="required"
:is-public="isPublic"
:metas="metas"
:column="column"
:password="password"
v-on="$listeners"
/>
@ -38,6 +39,7 @@
:breadcrumbs="breadcrumbs"
:is-locked="isLocked"
:required="required"
:column="column"
:metas="metas"
:password="password"
v-on="$listeners"
@ -60,6 +62,7 @@
:breadcrumbs="breadcrumbs"
:is-locked="isLocked"
:metas="metas"
:column="column"
:password="password"
v-on="$listeners"
/>
@ -69,6 +72,7 @@
:active="active"
:row="row"
:meta="meta"
:metas="metas"
:nodes="nodes"
:api="api"
:sql-ui="sqlUi"

3
packages/nc-gui/components/project/spreadsheet/components/virtualCell/belongsToCell.vue

@ -14,7 +14,7 @@
</template>
</div>
<div
v-if="!isLocked && _isUIAllowed('xcDatatableEditable')"
v-if="!isLocked && _isUIAllowed('xcDatatableEditable') && !isPublic"
class="action align-center justify-center px-1 flex-shrink-1"
:class="{'d-none': !active, 'd-flex':active }"
>
@ -60,6 +60,7 @@
}"
:bt="value"
:is-public="isPublic"
:row-id="parentId"
@new-record="showNewRecordModal"
@edit="editParent"
@unlink="unlink"

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

@ -1,6 +1,9 @@
<template>
<!-- <v-dialog v-model="show" width="600">-->
<v-card width="600" color="">
<pre class="caption">{{ parentMeta }}</pre>
<pre class="caption">{{ meta }}</pre>
<v-card-title v-if="!isForm" class="textColor--text mx-2" :class="{'py-2':isForm}">
<span v-if="!isForm">{{ meta ? meta._tn : 'Children' }}</span>
<v-spacer />
@ -144,7 +147,10 @@ export default {
size: Number,
api: [Object, Function],
mm: [Object, Boolean],
isPublic: Boolean
isPublic: Boolean,
rowId: [String, Number],
column: Object,
type: String
},
data: () => ({
data: null,
@ -173,6 +179,23 @@ export default {
},
methods: {
async loadData() {
if (this.isPublic && this.$route.params.id) {
this.data = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'sharedViewNestedChildDataGet', {
password: this.password,
limit: this.size,
tn: this.tn,
view_id: this.$route.params.id,
row_id: this.rowId,
offset: this.size * (this.page - 1),
query: this.query,
_cn: this.column._cn,
ptn: this.parentMeta.tn,
ctn: this.meta.tn,
type: this.type
}])
return
}
if (!this.api || this.isNew) { return }
this.data = await this.api.paginatedList({
limit: this.size,

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

@ -6,11 +6,13 @@
<list-child-items
v-if="show"
ref="child"
:type="type"
:row-id="rowId"
:local-state="localState"
:is-new="isNew"
:size="10"
:meta="meta"
:parent-meta="meta"
:parent-meta="parentMeta"
:primary-col="primaryCol"
:primary-key="primaryKey"
:api="api"
@ -18,6 +20,7 @@
v-bind="$attrs"
:read-only="readOnly"
:is-public="isPublic"
column="column"
v-on="$listeners"
/>
</v-dialog>
@ -30,6 +33,7 @@ export default {
name: 'ListChildItemsModal',
components: { ListChildItems },
props: {
type: String,
readOnly: Boolean,
localState: Array,
isNew: Boolean,
@ -51,7 +55,9 @@ export default {
size: Number,
api: [Object, Function],
mm: [Object, Boolean],
isPublic: Boolean
isPublic: Boolean,
rowId: [String, Number],
column: Object
},
data: () => ({
data: null,

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

@ -28,7 +28,7 @@
:class="{'d-none': !active, 'd-flex':active }"
>
<x-icon
v-if="_isUIAllowed('xcDatatableEditable')"
v-if="_isUIAllowed('xcDatatableEditable') && !isPublic"
small
:color="['primary','grey']"
@click="showNewRecordModal"
@ -76,11 +76,14 @@
:primary-col="childPrimaryCol"
:primary-key="childPrimaryKey"
:api="childApi"
:column="column"
:query-params="{
...childQueryParams,
where: `(${childForeignKey},eq,${parentId})`
}"
:is-public="isPublic"
:row-id="parentId"
type="hm"
@new-record="showNewRecordModal"
@edit="editChild"
@unlink="unlinkChild"
@ -174,7 +177,8 @@ export default {
required: Boolean,
isPublic: Boolean,
metas: Object,
password: String
password: String,
column: Object
},
data: () => ({
newRecordModal: false,

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

@ -44,16 +44,17 @@
<script>
// import ApiFactory from '@/components/project/spreadsheet/apis/apiFactory'
import TableCell from '../cell'
import ItemChip from '@/components/project/spreadsheet/components/virtualCell/components/itemChip'
import ListChildItemsModal
from '@/components/project/spreadsheet/components/virtualCell/components/listChildItemsModal'
import TableCell from '../cell'
export default {
name: 'LookupCell',
components: { TableCell, ListChildItemsModal, ItemChip },
props: {
meta: [Object],
metas: [Object],
column: [Object],
nodes: [Object],
row: [Object],
@ -76,22 +77,22 @@ export default {
})
},
lookUpMeta() {
return this.$store.state.meta.metas[this.column.lk.ltn]
return this.metas ? this.metas[this.column.lk.ltn] : this.$store.state.meta.metas[this.column.lk.ltn]
},
assocMeta() {
return this.column.lk.type === 'mm' && this.$store.state.meta.metas[this.column.lk.vtn]
return this.column.lk.type === 'mm' && (this.metas ? this.metas[this.column.lk.vtn] : this.$store.state.meta.metas[this.column.lk.vtn])
},
lookUpColumnAlias() {
if (!this.lookUpMeta || !this.column.lk.lcn) {
return
}
return (this.$store.state.meta.metas[this.column.lk.ltn].columns.find(cl => cl.cn === this.column.lk.lcn) || {})._cn
return (this.lookUpMeta.columns.find(cl => cl.cn === this.column.lk.lcn) || {})._cn
},
lookUpColumn() {
if (!this.lookUpMeta || !this.column.lk.lcn) {
return
}
return (this.$store.state.meta.metas[this.column.lk.ltn].columns.find(cl => cl.cn === this.column.lk.lcn) || {})
return (this.lookUpMeta.columns.find(cl => cl.cn === this.column.lk.lcn) || {})
},
localValueObj() {
if (!this.column || !this.row) {
@ -146,14 +147,14 @@ export default {
},
methods: {
async loadLookupMeta() {
if (!this.lookUpMeta) {
if (!this.metas && !this.lookUpMeta) {
await this.$store.dispatch('meta/ActLoadMeta', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.column.lk.ltn
})
}
if (this.column.lk.type === 'mm' && !this.assocMeta) {
if (!this.metas && this.column.lk.type === 'mm' && !this.assocMeta) {
await this.$store.dispatch('meta/ActLoadMeta', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,

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

@ -22,7 +22,7 @@
:class="{'d-none': !active, 'd-flex':active }"
>
<x-icon
v-if="_isUIAllowed('xcDatatableEditable')"
v-if="_isUIAllowed('xcDatatableEditable') && !isPublic"
small
:color="['primary','grey']"
@click="showNewRecordModal"
@ -72,6 +72,8 @@
:query-params="{...childQueryParams, conditionGraph }"
:local-state="localState"
:is-public="isPublic"
:row-id="parentPrimaryKey"
type="mm"
@new-record="showNewRecordModal"
@edit="editChild"
@unlink="unlinkChild"
@ -84,7 +86,7 @@
:heading="confirmMessage"
/>
<!-- todo : move to listitem component -->
<!-- todo : move to list item component -->
<v-dialog
v-if="selectedChild && !isPublic"
v-model="expandFormModal"

112
packages/nc-gui/components/project/spreadsheet/public/xcTable.vue

@ -4,7 +4,7 @@
<span class="font-weight-bold"> {{ modelName }}</span> <span class="font-weight-regular ml-1">( Main View )</span>
</div>
<v-toolbar height="40" dense class="elevation-0 xc-toolbar xc-border-bottom" style="z-index: 7;border-radius: 4px">
<v-toolbar v-if="meta" height="40" dense class="elevation-0 xc-toolbar xc-border-bottom" style="z-index: 7;border-radius: 4px">
<div class="d-flex xc-border align-center search-box">
<v-menu bottom offset-y>
<template #activator="{on}">
@ -107,6 +107,7 @@
</v-toolbar>
<div
v-if="meta"
:class="`cell-height-${cellHeight}`"
style="overflow:auto;transition: width 500ms "
class="d-flex"
@ -117,13 +118,14 @@
<xc-grid-view
v-else
is-public-view
:meta="meta"
:metas="metas"
:data="data"
:available-columns="availableColumns"
:show-fields="showFields"
:belongs-to="belongsTo"
:has-many="hasMany"
:is-public-view="true"
:nodes="{dbAlias:''}"
:sql-ui="sqlUi"
/>
@ -170,22 +172,21 @@
<script>
import ApiFactory from '@/components/project/spreadsheet/apis/apiFactory'
// import EditableCell from "@/components/project/spreadsheet/editableCell";
import spreadsheet from '../mixins/spreadsheet'
import ApiFactory from '../apis/apiFactory'
// import EditableCell from "../editableCell";
import FieldsMenu from '../components/fieldsMenu'
import SortListMenu from '../components/sortListMenu'
import ColumnFilterMenu from '../components/columnFilterMenu'
import XcGridView from '../views/xcGridView'
import { SqlUI } from '@/helpers/sqlUi'
import FieldsMenu from '@/components/project/spreadsheet/components/fieldsMenu'
import SortListMenu from '@/components/project/spreadsheet/components/sortListMenu'
import ColumnFilterMenu from '@/components/project/spreadsheet/components/columnFilterMenu'
import XcGridView from '@/components/project/spreadsheet/views/xcGridView'
import spreadsheet from '@/components/project/spreadsheet/mixins/spreadsheet'
// import ExpandedForm from "@/components/project/spreadsheet/expandedForm";
// import ExpandedForm from "../expandedForm";
export default {
name: 'XcTable',
components: { XcGridView, ColumnFilterMenu, SortListMenu, FieldsMenu },
mixins: [spreadsheet],
props: {
dbAlias: String,
env: String,
nodes: Object,
addNewRelationTab: Function,
@ -196,6 +197,7 @@ export default {
relationPrimaryValue: [String, Number]
},
data: () => ({
metas: {},
fieldsOrder: [],
password: null,
showPasswordModal: false,
@ -211,7 +213,7 @@ export default {
showExpandModal: false,
selectedExpandRowIndex: null,
selectedExpandRowMeta: null,
meta: [],
meta: null,
navDrawer: true,
selected: {
row: null,
@ -361,7 +363,7 @@ export default {
},
async mounted() {
try {
// await this.loadMeta();
await this.loadMetaData()
await this.loadTableData()
// const {list, count} = await this.api.paginatedList(this.queryParams);
// this.count = count;
@ -471,34 +473,100 @@ export default {
})
this.filters = this.filters.slice()
},
async loadMetaData() {
this.loading = true
try {
// eslint-disable-next-line camelcase
const {
meta,
// model_name,
client,
query_params: qp,
db_alias: dbAlias,
relatedTableMetas
} = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'sharedViewGet', {
view_id: this.$route.params.id,
password: this.password
}])
this.client = client
this.meta = meta
this.query_params = JSON.parse(qp)
this.dbAlias = dbAlias
this.metas = relatedTableMetas
this.showFields = this.query_params.showFields || {}
this.fieldList = Object.keys(this.showFields)
let fields = this.query_params.fieldsOrder || []
if (!fields.length) { fields = Object.keys(this.showFields) }
// eslint-disable-next-line camelcase
let columns = this.meta.columns
if (this.meta && this.meta.v) {
columns = [...columns, ...this.meta.v.map(v => ({ ...v, virtual: 1 }))]
}
{
const _ref = {}
columns.forEach((c) => {
if (c.virtual && c.bt) {
c.prop = `${c.bt.rtn}Read`
}
if (c.virtual && c.mm) {
c.prop = `${c.mm.rtn}MMList`
}
if (c.virtual && c.hm) {
c.prop = `${c.hm.tn}List`
}
if (c.virtual && c.lk) {
c.alias = `${c.lk._lcn} (from ${c.lk._ltn})`
} else {
c.alias = c._cn
}
if (c.alias in _ref) {
c.alias += _ref[c.alias]++
} else {
_ref[c.alias] = 1
}
})
}
} catch (e) {
if (e.message === 'Not found') {
this.notFound = true
} else if (e.message === 'Invalid password') {
this.showPasswordModal = true
}
}
this.loadingData = false
},
async loadTableData() {
this.loadingData = true
try {
// eslint-disable-next-line camelcase
const { data: list, count, meta, model_name, client } = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
const { data: list, count, meta, model_name, client, queryParams } = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
query: this.queryParams
}, 'getSharedViewData', {
view_id: this.$route.params.id,
password: this.password
}])
this.client = client
this.meta = meta
// this.showFields = queryParams && queryParams.showFields
// this.meta = meta
// eslint-disable-next-line camelcase
this.modelName = model_name
this.fieldList = this.meta.columns.map(c => c._cn)
this.count = count
this.data = list.map(row => ({
row,
oldRow: { ...row },
rowMeta: {}
}))
this.showFields = this.fieldList.reduce((obj, k) => {
obj[k] = true
return obj
}, {})
} catch (e) {
this.showPasswordModal = true
}

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

@ -151,7 +151,9 @@
>
<virtual-cell
v-if="columnObj.virtual"
:is-locked="isLocked"
:is-public="isPublicView"
:metas="metas"
:is-locked="isLocked "
:column="columnObj"
:row="rowObj"
:nodes="nodes"
@ -252,6 +254,7 @@ export default {
},
mixins: [colors],
props: {
metas: Object,
relationType: String,
availableColumns: [Object, Array],
showFields: Object,

6
packages/nocodb/src/lib/dataMapper/lib/sql/CustomKnex.ts

@ -450,7 +450,11 @@ declare module 'knex' {
_and: XcXonditionObj[];
_not: XcXonditionObj;
[key: string]: XcXonditionObj | XcXonditionObj[];
[key: string]:
| XcXonditionObj
| XcXonditionObj[]
| XcConditionObjVal
| XcConditionObjVal[];
}
interface QueryBuilder {

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

@ -1314,6 +1314,9 @@ export default class NcMetaMgr {
case 'sharedViewNestedDataGet':
result = await this.sharedViewNestedDataGet(req, args);
break;
case 'sharedViewNestedChildDataGet':
result = await this.sharedViewNestedChildDataGet(req, args);
break;
// case 'submitSharedViewFormData':
// result = await this.submitSharedViewFormData(req, args);
@ -3291,18 +3294,18 @@ export default class NcMetaMgr {
protected async createSharedViewLink(req, args: any): Promise<any> {
try {
if (args.args.query_params?.fields) {
const fields = args.args.query_params?.fields.split(',');
args.args.meta.columns = args.args.meta.columns.filter(c =>
fields.includes(c._cn)
);
}
// if (args.args.query_params?.fields) {
// const fields = args.args.query_params?.fields.split(',');
// args.args.meta.columns = args.args.meta.columns.filter(c =>
// fields.includes(c._cn)
// );
// }
const insertData = {
project_id: args.project_id,
db_alias: this.getDbAlias(args),
model_name: args.args.model_name,
meta: JSON.stringify(args.args.meta),
// meta: JSON.stringify(args.args.meta),
query_params: JSON.stringify(args.args.query_params),
view_id: uuidv4()
// password: args.args.password
@ -3505,6 +3508,104 @@ export default class NcMetaMgr {
}
}
protected async sharedViewNestedChildDataGet(_req, args: any): Promise<any> {
try {
const viewMeta = await this.xcMeta
.knex('nc_shared_views')
.where({
view_id: args.args.view_id
})
.first();
if (!viewMeta) {
throw new Error('Not found');
}
if (
viewMeta &&
viewMeta.password &&
viewMeta.password !== args.args.password
) {
throw new Error(this.INVALID_PASSWORD_ERROR);
}
const tn = args.args?.ctn;
// @ts-ignore
// const queryParams = JSON.parse(viewMeta.query_params);
const apiBuilder = this.app?.projectBuilders
?.find(pb => pb.id === viewMeta.project_id)
?.apiBuilders?.find(ab => ab.dbAlias === viewMeta.db_alias);
// todo: only allow related table
// if(tn &&){
//
// }
const model = apiBuilder.xcModels?.[tn];
const meta = apiBuilder.getMeta(tn);
const primaryCol = apiBuilder?.getMeta(tn)?.columns?.find(c => c.pv)?.cn;
const commonParams: any =
primaryCol && args.args.query
? {
condition: {
[primaryCol]: {
like: `%${args.args.query}%`
}
}
}
: {};
switch (args.args?.type) {
case 'mm':
{
const mm = meta.v.find(v => v.mm && v._cn === args.args._cn)?.mm;
const assocMeta = apiBuilder.getMeta(mm.vtn);
commonParams.conditionGraph = {
condition: {
[assocMeta.tn]: {
relationType: 'hm',
[assocMeta.columns.find(c => c.cn === mm.vcn).cn]: {
eq: args.arags.row_id
}
}
},
models: apiBuilder?.xcModels
};
}
break;
case 'hm':
{
const hm = meta.v.find(v => v.hm && v._cn === args.args._cn)?.hm;
// const childMeta = apiBuilder.getMeta(hm.rtn);
commonParams.condition = {
[hm.rcn]: {
eq: args.arags.row_id
}
};
}
break;
}
return {
list: await model?.list({
fields: model.getTablePKandPVFields(),
limit: args.args.limit,
offset: args.args.offset,
...commonParams
}),
count: (await model?.countByPk(commonParams as any))?.count
};
} catch (e) {
console.log(e);
throw e;
}
}
protected async sharedViewInsert(req, args: any): Promise<any> {
const viewMeta = await this.xcMeta
.knex('nc_shared_views')
@ -3598,6 +3699,10 @@ export default class NcMetaMgr {
} else if (v.mm) {
tn = v.mm.rtn;
relatedTableMetas[v.mm.vtn] = apiBuilder?.getMeta(v.mm.vtn);
} else if (v.lk) {
tn = v.lk.ltn;
if (v.lk.vtn)
relatedTableMetas[v.lk.vtn] = apiBuilder?.getMeta(v.lk.vtn);
}
relatedTableMetas[tn] = apiBuilder?.getMeta(tn);
}

8
packages/nocodb/src/lib/noco/meta/NcMetaMgrEE.ts

@ -142,7 +142,10 @@ export default class NcMetaMgrEE extends NcMetaMgr {
}
const queryParams = JSON.parse(viewMeta.query_params);
if (!meta) throw new Error('Meta not found');
if (!meta) {
throw new Error('Meta not found');
}
meta = {
...meta,
columns: meta.columns.filter(c => queryParams?.showFields?.[c._cn])
@ -163,7 +166,8 @@ export default class NcMetaMgrEE extends NcMetaMgr {
return {
model_name: viewMeta.model_name,
meta,
data: await model.list({
queryParams,
data: await model.nestedList({
...req.query,
where,
fields

Loading…
Cancel
Save