Browse Source

fix: Removed No Views placeholder and now we keep hasNonDefaultViews in table meta and wrote unit tests

pull/6539/head
Muhammed Mustafa 1 year ago
parent
commit
7220194f4b
  1. 5
      packages/nc-gui/components/dashboard/TreeView/CreateViewBtn.vue
  2. 9
      packages/nc-gui/components/dashboard/TreeView/TableNode.vue
  3. 19
      packages/nc-gui/components/dashboard/TreeView/ViewsList.vue
  4. 1
      packages/nc-gui/lang/en.json
  5. 15
      packages/nc-gui/store/tables.ts
  6. 47
      packages/nocodb/src/models/Model.ts
  7. 10
      packages/nocodb/src/models/View.ts
  8. 14
      packages/nocodb/tests/unit/factory/table.ts
  9. 16
      packages/nocodb/tests/unit/factory/view.ts
  10. 1
      packages/nocodb/tests/unit/rest/tests/attachment.test.ts
  11. 81
      packages/nocodb/tests/unit/rest/tests/table.test.ts

5
packages/nc-gui/components/dashboard/TreeView/CreateViewBtn.vue

@ -47,6 +47,11 @@ function onOpenModal({
force: true, force: true,
}) })
table.value.meta = {
...(table.value.meta as object),
hasNonDefaultViews: true,
}
navigateToView({ navigateToView({
view, view,
tableId: table.value.id!, tableId: table.value.id!,

9
packages/nc-gui/components/dashboard/TreeView/TableNode.vue

@ -167,13 +167,20 @@ const isTableOpened = computed(() => {
@click="onOpenTable" @click="onOpenTable"
> >
<div class="flex flex-row h-full items-center"> <div class="flex flex-row h-full items-center">
<NcButton type="text" size="xxsmall" class="nc-sidebar-node-btn nc-sidebar-expand" @click.stop="onExpand"> <NcButton
v-if="(table.meta as any)?.hasNonDefaultViews"
type="text"
size="xxsmall"
class="nc-sidebar-node-btn nc-sidebar-expand"
@click.stop="onExpand"
>
<GeneralIcon <GeneralIcon
icon="triangleFill" icon="triangleFill"
class="nc-sidebar-base-node-btns group-hover:visible invisible cursor-pointer transform transition-transform duration-500 h-1.5 w-1.5 !text-gray-600 rotate-90" class="nc-sidebar-base-node-btns group-hover:visible invisible cursor-pointer transform transition-transform duration-500 h-1.5 w-1.5 !text-gray-600 rotate-90"
:class="{ '!rotate-180': isExpanded }" :class="{ '!rotate-180': isExpanded }"
/> />
</NcButton> </NcButton>
<div v-else class="min-w-5.75"></div>
<div class="flex w-auto" :data-testid="`tree-view-table-draggable-handle-${table.title}`"> <div class="flex w-auto" :data-testid="`tree-view-table-draggable-handle-${table.title}`">
<div <div
class="flex items-center nc-table-icon" class="flex items-center nc-table-icon"

19
packages/nc-gui/components/dashboard/TreeView/ViewsList.vue

@ -277,6 +277,13 @@ function openDeleteDialog(view: ViewType) {
tableId: table.value.id!, tableId: table.value.id!,
force: true, force: true,
}) })
const activeNonDefaultViews = viewsByTable.value.get(table.value.id!)?.filter((v) => !v.is_default) ?? []
table.value.meta = {
...(table.value.meta as object),
hasNonDefaultViews: activeNonDefaultViews.length > 1,
}
}, },
}) })
@ -356,18 +363,8 @@ function onOpenModal({
</script> </script>
<template> <template>
<div
v-if="!views.length"
class="text-gray-500 my-1.75 xs:(my-2.5 text-base)"
:class="{
'ml-19.25 xs:ml-22.25': isDefaultBase,
'ml-24.75 xs:ml-30': !isDefaultBase,
}"
>
{{ $t('labels.noViews') }}
</div>
<a-menu <a-menu
v-if="views.length"
ref="menuRef" ref="menuRef"
:class="{ dragging }" :class="{ dragging }"
class="nc-views-menu flex flex-col w-full !border-r-0 !bg-inherit" class="nc-views-menu flex flex-col w-full !border-r-0 !bg-inherit"

1
packages/nc-gui/lang/en.json

@ -436,7 +436,6 @@
"untitledToken": "Untitled token", "untitledToken": "Untitled token",
"tableName": "Table name", "tableName": "Table name",
"dashboardName": "Dashboard name", "dashboardName": "Dashboard name",
"noViews": "No views",
"createView": "Create a View", "createView": "Create a View",
"creatingView": "Creating View", "creatingView": "Creating View",
"duplicateView": "Duplicate view", "duplicateView": "Duplicate view",

15
packages/nc-gui/store/tables.ts

@ -76,6 +76,21 @@ export const useTablesStore = defineStore('tablesStore', () => {
includeM2M: includeM2M.value, includeM2M: includeM2M.value,
}) })
tables.list?.forEach((t) => {
let meta = t.meta
if (typeof meta === 'string') {
try {
meta = JSON.parse(meta)
} catch (e) {
console.error(e)
}
}
if (!meta) meta = {}
t.meta = meta
})
projectTables.value.set(projectId, tables.list || []) projectTables.value.set(projectId, tables.list || [])
} }

47
packages/nocodb/src/models/Model.ts

@ -221,6 +221,19 @@ export default class Model implements TableType {
(a.order != null ? a.order : Infinity) - (a.order != null ? a.order : Infinity) -
(b.order != null ? b.order : Infinity), (b.order != null ? b.order : Infinity),
); );
for (const model of modelList) {
if (model.meta?.hasNonDefaultViews === undefined) {
model.meta = {
...(model.meta ?? {}),
hasNonDefaultViews: await Model.getNonDefaultViewsCountAndReset(
{ modelId: model.id },
ncMeta,
),
};
}
}
return modelList.map((m) => new Model(m)); return modelList.map((m) => new Model(m));
} }
@ -255,6 +268,18 @@ export default class Model implements TableType {
await NocoCache.setList(CacheScope.MODEL, [project_id], modelList); await NocoCache.setList(CacheScope.MODEL, [project_id], modelList);
} }
for (const model of modelList) {
if (model.meta?.hasNonDefaultViews === undefined) {
model.meta = {
...(model.meta ?? {}),
hasNonDefaultViews: await Model.getNonDefaultViewsCountAndReset(
{ modelId: model.id },
ncMeta,
),
};
}
}
return modelList.map((m) => new Model(m)); return modelList.map((m) => new Model(m));
} }
@ -964,4 +989,26 @@ export default class Model implements TableType {
tableId, tableId,
); );
} }
static async getNonDefaultViewsCountAndReset(
{
modelId,
}: {
modelId: string;
},
_ncMeta = Noco.ncMeta,
) {
const model = await this.get(modelId);
let modelMeta = parseMetaProp(model);
const views = await View.list(modelId);
modelMeta = {
...(modelMeta ?? {}),
hasNonDefaultViews: views.length > 1,
};
await this.updateMeta(modelId, modelMeta);
return modelMeta?.hasNonDefaultViews;
}
} }

10
packages/nocodb/src/models/View.ts

@ -501,6 +501,11 @@ export default class View implements ViewType {
} }
} }
await Model.getNonDefaultViewsCountAndReset(
{ modelId: view.fk_model_id },
ncMeta,
);
return View.get(view_id, ncMeta); return View.get(view_id, ncMeta);
} }
@ -1078,6 +1083,11 @@ export default class View implements ViewType {
CacheScope.SINGLE_QUERY, CacheScope.SINGLE_QUERY,
`${view.fk_model_id}:${view.id}:*`, `${view.fk_model_id}:${view.id}:*`,
); );
await Model.getNonDefaultViewsCountAndReset(
{ modelId: view.fk_model_id },
ncMeta,
);
} }
private static extractViewColumnsTableName(view: View) { private static extractViewColumnsTableName(view: View) {

14
packages/nocodb/tests/unit/factory/table.ts

@ -45,4 +45,16 @@ const getAllTables = async ({ project }: { project: Project }) => {
return tables; return tables;
}; };
export { createTable, getTable, getAllTables }; const updateTable = async (
context,
{ table, args }: { table: Model; args: any },
) => {
const response = await request(context.app)
.patch(`/api/v1/db/meta/tables/${table.id}`)
.set('xc-auth', context.token)
.send(args);
return response.body;
};
export { createTable, getTable, getAllTables, updateTable };

16
packages/nocodb/tests/unit/factory/view.ts

@ -121,4 +121,18 @@ const updateView = async (
} }
}; };
export { createView, updateView, getView }; const deleteView = async (
context,
{
viewId,
}: {
viewId: string;
},
) => {
await request(context.app)
.delete(`/api/v1/db/meta/views/${viewId}`)
.set('xc-auth', context.token)
.expect(200);
};
export { createView, updateView, getView, deleteView };

1
packages/nocodb/tests/unit/rest/tests/attachment.test.ts

@ -14,7 +14,6 @@ function attachmentTests() {
beforeEach(async function () { beforeEach(async function () {
console.time('#### attachmentTests'); console.time('#### attachmentTests');
context = await init();
fs.writeFileSync(FILE_PATH, 'test', `utf-8`); fs.writeFileSync(FILE_PATH, 'test', `utf-8`);
context = await init(); context = await init();
console.timeEnd('#### attachmentTests'); console.timeEnd('#### attachmentTests');

81
packages/nocodb/tests/unit/rest/tests/table.test.ts

@ -1,11 +1,13 @@
import 'mocha'; import 'mocha';
import request from 'supertest'; import request from 'supertest';
import { expect } from 'chai';
import { createView, deleteView } from 'tests/unit/factory/view';
import { ViewTypes } from 'nocodb-sdk';
import init from '../../init'; import init from '../../init';
import { createTable, getAllTables } from '../../factory/table'; import { createTable, getAllTables, updateTable } from '../../factory/table';
import { createProject } from '../../factory/project'; import { createProject } from '../../factory/project';
import { defaultColumns } from '../../factory/column'; import { defaultColumns } from '../../factory/column';
import Model from '../../../../src/models/Model'; import Model from '../../../../src/models/Model';
import { expect } from 'chai';
// Test case list // Test case list
// 1. Get table list // 1. Get table list
@ -277,6 +279,81 @@ function tableTest() {
// new Error(); // new Error();
// }); // });
}); });
it('Add and delete view should update hasNonDefaultViews', async () => {
let response = await request(context.app)
.get(`/api/v1/db/meta/projects/${project.id}/tables`)
.set('xc-auth', context.token)
.send({})
.expect(200);
expect(response.body.list[0].meta.hasNonDefaultViews).to.be.false;
const view = await createView(context, {
table,
title: 'view1',
type: ViewTypes.GRID,
});
response = await request(context.app)
.get(`/api/v1/db/meta/projects/${project.id}/tables`)
.set('xc-auth', context.token)
.send({})
.expect(200);
expect(response.body.list[0].meta.hasNonDefaultViews).to.be.true;
await deleteView(context, { viewId: view.id });
response = await request(context.app)
.get(`/api/v1/db/meta/projects/${project.id}/tables`)
.set('xc-auth', context.token)
.send({})
.expect(200);
expect(response.body.list[0].meta.hasNonDefaultViews).to.be.false;
});
it('Project with empty meta should update hasNonDefaultViews', async () => {
let response = await request(context.app)
.get(`/api/v1/db/meta/projects/${project.id}/tables`)
.set('xc-auth', context.token)
.send({})
.expect(200);
expect(response.body.list[0].meta.hasNonDefaultViews).to.be.false;
const view = await createView(context, {
table,
title: 'view1',
type: ViewTypes.GRID,
});
await updateTable(context, {
table,
args: {
meta: {},
},
});
response = await request(context.app)
.get(`/api/v1/db/meta/projects/${project.id}/tables`)
.set('xc-auth', context.token)
.send({})
.expect(200);
expect(response.body.list[0].meta.hasNonDefaultViews).to.be.true;
await deleteView(context, { viewId: view.id });
response = await request(context.app)
.get(`/api/v1/db/meta/projects/${project.id}/tables`)
.set('xc-auth', context.token)
.send({})
.expect(200);
expect(response.body.list[0].meta.hasNonDefaultViews).to.be.false;
});
} }
export default async function () { export default async function () {

Loading…
Cancel
Save