diff --git a/packages/app/package.json b/packages/app/package.json index 1fe8d6015..02a53358e 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -33,12 +33,14 @@ "@nocobase/server": "^0.3.0-alpha.0", "@umijs/preset-react": "1.x", "@umijs/test": "^3.2.23", + "array-move": "^3.0.1", "concurrently": "^5.3.0", "lint-staged": "^10.0.7", "nodemon": "^2.0.6", "prettier": "^1.19.1", "react": "^16.12.0", "react-dom": "^16.12.0", + "react-sortable-hoc": "^1.11.0", "styled-components": "^5.2.1", "umi": "^3.2.23", "yorkie": "^2.0.0" diff --git a/packages/app/src/api/index.ts b/packages/app/src/api/index.ts index dc343b2b4..6c06106ff 100644 --- a/packages/app/src/api/index.ts +++ b/packages/app/src/api/index.ts @@ -43,7 +43,7 @@ const data = { path: '/', type: 'layout', template: 'TopMenuLayout', - order: 1, + sort: 10, children: [ { title: '仪表盘', @@ -51,7 +51,7 @@ const data = { path: '/dashboard', icon: 'dashboard', template: 'page1', - order: 2, + sort: 20, }, { title: '数据', @@ -59,7 +59,7 @@ const data = { path: '/collections', icon: 'dashboard', template: 'SideMenuLayout', - order: 3, + sort: 30, children: [ { title: '页面3', @@ -67,7 +67,7 @@ const data = { path: '/collections/page3', icon: 'dashboard', template: 'page3', - order: 5, + sort: 40, }, { title: '页面4', @@ -75,17 +75,17 @@ const data = { path: '/collections/page4', icon: 'dashboard', template: 'page4', - order: 6, - }, - { - title: '页面5', - type: 'collection', - path: '/collections/collection1', - icon: 'dashboard', - template: 'collection', - collection: 'collection1', - order: 7, + sort: 50, }, + // { + // title: '页面5', + // type: 'collection', + // path: '/collections/collection1', + // icon: 'dashboard', + // template: 'collection', + // collection: 'collection1', + // sort: 60, + // }, ] }, { @@ -94,7 +94,7 @@ const data = { path: '/users', icon: 'dashboard', template: 'SideMenuLayout', - order: 3, + sort: 70, children: [ { title: '用户管理', @@ -103,7 +103,7 @@ const data = { icon: 'dashboard', template: 'collection', collection: 'users', - order: 1, + sort: 80, }, ] }, @@ -113,7 +113,7 @@ const data = { path: '/settings', icon: 'dashboard', template: 'SideMenuLayout', - order: 4, + sort: 90, children: [ { title: '页面与菜单', @@ -121,7 +121,7 @@ const data = { collection: 'pages', path: '/settings/pages', icon: 'dashboard', - order: 1, + sort: 100, }, { title: '数据表配置', @@ -129,7 +129,7 @@ const data = { collection: 'collections', path: '/settings/collections', icon: 'dashboard', - order: 2, + sort: 110, }, { title: '权限配置', @@ -137,7 +137,7 @@ const data = { collection: 'roles', path: '/settings/roles', icon: 'dashboard', - order: 3, + sort: 120, }, ] }, @@ -173,117 +173,121 @@ const data = { // console.log(options); const associations: any = {}; if (options.fields) { - associations['fields'] = options.fields.map(item => ({ + associations['fields'] = options.fields.map((item, sort) => ({ ...item, options: item, + sort, })) } if (options.tabs) { - associations['tabs'] = options.tabs.map(item => ({ + associations['tabs'] = options.tabs.map((item, sort) => ({ ...item, options: item, + sort, })) } if (options.actions) { - associations['actions'] = options.actions.map(item => ({ + associations['actions'] = options.actions.map((item, sort) => ({ ...item, options: item, + sort, })) } if (options.views) { - associations['views'] = options.views.map(item => ({ + associations['views'] = options.views.map((item, sort) => ({ ...item, options: item, + sort, })) } await collection.updateAssociations(associations); } - const actions = await Action.findAll(); + // const actions = await Action.findAll(); - for (const action of actions) { - const viewName = action.options.viewName; - console.log({viewName}); - if (viewName) { - const view = await View.findOne({ - where: { - name: viewName, - collection_name: action.collection_name - }, - }); - if (view) { - action.options.viewId = view.id; - console.log(action.options); - action.setDataValue('options', action.options); - action.changed('options', true); - await action.save(); - } - } - } - const tabs = await Tab.findAll(); + // for (const action of actions) { + // const viewName = action.options.viewName; + // console.log({viewName}); + // if (viewName) { + // const view = await View.findOne({ + // where: { + // name: viewName, + // collection_name: action.collection_name + // }, + // }); + // if (view) { + // action.options.viewId = view.id; + // console.log(action.options); + // action.setDataValue('options', action.options); + // action.changed('options', true); + // await action.save(); + // } + // } + // } + // const tabs = await Tab.findAll(); - for (const tab of tabs) { - const viewName = tab.options.viewName; - if (!viewName) { - continue; - } - let view: any; - if (tab.type === 'association') { - view = await View.findOne({ - where: { - name: viewName, - collection_name: tab.options.association, - }, - }); - } else { - view = await View.findOne({ - where: { - name: viewName, - collection_name: tab.collection_name, - }, - }); - } - if (view) { - tab.options.viewId = view.id; - tab.setDataValue('options', tab.options); - tab.changed('options', true); - await tab.save(); - } - } - const views = await View.findAll(); - for (const view of views) { - const detailsViewName = view.options.detailsViewName; - if (detailsViewName) { - const v = await View.findOne({ - where: { - name: detailsViewName, - collection_name: view.collection_name - }, - }); - if (v) { - view.options.detailsViewId = v.id; - view.setDataValue('options', view.options); - view.changed('options', true); - await view.save(); - } - } - const updateViewName = view.options.updateViewName; - if (updateViewName) { - const v = await View.findOne({ - where: { - name: updateViewName, - collection_name: view.collection_name - }, - }); - if (v) { - view.options.updateViewId = v.id; - view.setDataValue('options', view.options); - view.changed('options', true); - await view.save(); - } - } - console.log({detailsViewName, updateViewName}); - } + // for (const tab of tabs) { + // const viewName = tab.options.viewName; + // if (!viewName) { + // continue; + // } + // let view: any; + // if (tab.type === 'association') { + // view = await View.findOne({ + // where: { + // name: viewName, + // collection_name: tab.options.association, + // }, + // }); + // } else { + // view = await View.findOne({ + // where: { + // name: viewName, + // collection_name: tab.collection_name, + // }, + // }); + // } + // if (view) { + // tab.options.viewId = view.id; + // tab.setDataValue('options', tab.options); + // tab.changed('options', true); + // await tab.save(); + // } + // } + // const views = await View.findAll(); + // for (const view of views) { + // const detailsViewName = view.options.detailsViewName; + // if (detailsViewName) { + // const v = await View.findOne({ + // where: { + // name: detailsViewName, + // collection_name: view.collection_name + // }, + // }); + // if (v) { + // view.options.detailsViewId = v.id; + // view.setDataValue('options', view.options); + // view.changed('options', true); + // await view.save(); + // } + // } + // const updateViewName = view.options.updateViewName; + // if (updateViewName) { + // const v = await View.findOne({ + // where: { + // name: updateViewName, + // collection_name: view.collection_name + // }, + // }); + // if (v) { + // view.options.updateViewId = v.id; + // view.setDataValue('options', view.options); + // view.changed('options', true); + // await view.save(); + // } + // } + // console.log({detailsViewName, updateViewName}); + // } // for (let table of tables) { // const options = table.getOptions(); diff --git a/packages/app/src/components/actions/Create.tsx b/packages/app/src/components/actions/Create.tsx index f3c718f3f..1551b2e72 100644 --- a/packages/app/src/components/actions/Create.tsx +++ b/packages/app/src/components/actions/Create.tsx @@ -4,28 +4,29 @@ import ViewFactory from '@/components/views'; export function Create(props) { console.log(props); - const { title, viewCollectionName, viewName } = props.schema; + const { title, viewName, collection_name } = props.schema; const { activeTab = {}, item = {} } = props; - const { association, collection_name } = activeTab; + const { association } = activeTab; const params = {}; if (association) { params['resourceName'] = association; - params['associatedName'] = collection_name; + params['associatedName'] = activeTab.collection_name; params['associatedKey'] = item.itemId; } else { params['resourceName'] = collection_name; params['resourceKey'] = item.itemId; } + console.log(params); + const drawerRef = useRef(); return ( <> diff --git a/packages/app/src/components/actions/Update.tsx b/packages/app/src/components/actions/Update.tsx index 31fbbab4d..32a8ce8a9 100644 --- a/packages/app/src/components/actions/Update.tsx +++ b/packages/app/src/components/actions/Update.tsx @@ -4,15 +4,15 @@ import ViewFactory from '@/components/views'; export function Update(props) { console.log(props); - const { title, viewCollectionName, viewName } = props.schema; + const { title, viewName, resourceName, collection_name } = props.schema; const { activeTab = {}, item = {} } = props; - const { association, collection_name } = activeTab; + const { association } = activeTab; const params = {}; if (association) { params['resourceName'] = association; - params['associatedName'] = collection_name; + params['associatedName'] = resourceName; params['associatedKey'] = item.itemId; } else { params['resourceName'] = collection_name; @@ -22,15 +22,15 @@ export function Update(props) { const drawerRef = useRef(); return ( <> - ) diff --git a/packages/app/src/components/pages/CollectionLoader/CollectionIndex.tsx b/packages/app/src/components/pages/CollectionLoader/CollectionIndex.tsx index ff1f6eb74..de8b5ffb6 100644 --- a/packages/app/src/components/pages/CollectionLoader/CollectionIndex.tsx +++ b/packages/app/src/components/pages/CollectionLoader/CollectionIndex.tsx @@ -29,8 +29,8 @@ export function CollectionIndex(props) { // } />
- diff --git a/packages/app/src/components/pages/CollectionLoader/CollectionSingle.tsx b/packages/app/src/components/pages/CollectionLoader/CollectionSingle.tsx index ebf8caad4..cb4f895f5 100644 --- a/packages/app/src/components/pages/CollectionLoader/CollectionSingle.tsx +++ b/packages/app/src/components/pages/CollectionLoader/CollectionSingle.tsx @@ -4,6 +4,7 @@ import { CollectionTabPane } from './CollectionTabPane'; import { getPathName, redirectTo } from './utils'; import api from '@/api-client'; import { useRequest } from 'umi'; +import { Spin } from '@nocobase/client'; export function CollectionSingle(props) { console.log(props); @@ -11,13 +12,16 @@ export function CollectionSingle(props) { const { tabs = [] } = props.collection; const activeTab = tabs.find(tab => tab.name == item.tabName)||{}; console.log(activeTab); - const { data = {} } = useRequest(() => activeTab && api.resource(activeTab.collection_name).getPageInfo({ + const { data = {}, loading } = useRequest(() => activeTab && api.resource(activeTab.collection_name).getPageInfo({ resourceKey: item.itemId, })); console.log(data); if (!activeTab) { return null; } + if (loading) { + return ; + } return (
diff --git a/packages/app/src/components/pages/CollectionLoader/index.tsx b/packages/app/src/components/pages/CollectionLoader/index.tsx index 20fb32c6a..1d194c109 100644 --- a/packages/app/src/components/pages/CollectionLoader/index.tsx +++ b/packages/app/src/components/pages/CollectionLoader/index.tsx @@ -34,7 +34,7 @@ export function CollectionLoader(props: any) { return (
- + { items.length === 0 && }
{items.length > 0 && (
diff --git a/packages/app/src/components/pages/Page.tsx b/packages/app/src/components/pages/Page.tsx new file mode 100644 index 000000000..392510dc9 --- /dev/null +++ b/packages/app/src/components/pages/Page.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { PageHeader } from 'antd'; +import './style.less'; + +export function Page(props: any) { + const { children, ...restProps } = props; + return ( +
+ +
+ {children} +
+
+ ); +}; + +export default Page; diff --git a/packages/app/src/components/pages/index.tsx b/packages/app/src/components/pages/index.tsx index 6a5caa7fe..2e28d625b 100644 --- a/packages/app/src/components/pages/index.tsx +++ b/packages/app/src/components/pages/index.tsx @@ -4,3 +4,4 @@ export { CollectionLoader } from './CollectionLoader'; export { PageLoader as default } from './PageLoader'; export { SideMenuLayout } from './SideMenuLayout/index'; export { TopMenuLayout } from './TopMenuLayout/index'; +export { Page } from './Page'; diff --git a/packages/app/src/components/pages/style.less b/packages/app/src/components/pages/style.less new file mode 100644 index 000000000..eecdc48cd --- /dev/null +++ b/packages/app/src/components/pages/style.less @@ -0,0 +1,3 @@ +.page-content { + margin: 24px; +} \ No newline at end of file diff --git a/packages/app/src/components/views/Details.tsx b/packages/app/src/components/views/Details.tsx new file mode 100644 index 000000000..7d047db70 --- /dev/null +++ b/packages/app/src/components/views/Details.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { Card, Descriptions, Button } from 'antd'; +import { Actions } from '@/components/actions'; +import api from '@/api-client'; +import { useRequest } from 'umi'; +import { Spin } from '@nocobase/client'; + +export function Details(props: any) { + const { + activeTab = {}, + pageInfo = {}, + schema, + resourceName, + associatedName, + associatedKey, + resourceKey, + } = props; + const { data = {}, loading } = useRequest(() => { + const name = associatedName ? `${associatedName}.${resourceName}` : resourceName; + return api.resource(name).get({ + resourceKey, + associatedKey, + }); + }); + console.log(props); + const { actions = [], fields = [] } = props.schema; + return ( + + + {loading ? : ( + + {fields.map((field: any) => ( + {data[field.name]} + ))} + + )} + + ); +} diff --git a/packages/app/src/components/views/Form/DrawerForm.tsx b/packages/app/src/components/views/Form/DrawerForm.tsx index 6519f2135..f31cbaf8d 100644 --- a/packages/app/src/components/views/Form/DrawerForm.tsx +++ b/packages/app/src/components/views/Form/DrawerForm.tsx @@ -14,15 +14,37 @@ import { setValidationLanguage, } from '@formily/antd'; import { QuestionCircleOutlined } from '@ant-design/icons'; +import { useRequest } from 'umi'; +import api from '@/api-client'; +import { Spin } from '@nocobase/client'; export const DrawerForm = forwardRef((props: any, ref) => { console.log(props); + const { + activeTab = {}, + pageInfo = {}, + schema, + resourceName, + associatedName, + associatedKey, + } = props; const [visible, setVisible] = useState(false); + const { data, run, loading } = useRequest((resourceKey) => { + const name = associatedName ? `${associatedName}.${resourceName}` : resourceName; + return api.resource(name).get({ + resourceKey, + associatedKey, + }); + }, { + manual: true, + }); useImperativeHandle(ref, () => ({ setVisible, + getData: run, })); const actions = createAsyncFormActions(); - const { title } = props.schema||{}; + const { title, fields: properties ={} } = props.schema||{}; + console.log({properties}); return ( { }}>提交 ]} > - : ( + - - - ); - }, - }} - > - + tooltip(title: string, offset = 3) { + return ( + + + + ); + }, + }} + > + + )} ); }); diff --git a/packages/app/src/components/views/SimpleTable.tsx b/packages/app/src/components/views/SimpleTable.tsx index 5931275f8..23717c089 100644 --- a/packages/app/src/components/views/SimpleTable.tsx +++ b/packages/app/src/components/views/SimpleTable.tsx @@ -1,38 +1,11 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { Table as AntdTable, Card } from 'antd'; -import { redirectTo } from '@/components/pages/CollectionLoader/utils'; import { Actions } from '@/components/actions'; import ViewFactory from '@/components/views'; -import { request, useRequest } from 'umi'; +import { useRequest } from 'umi'; import api from '@/api-client'; - -const dataSource = []; -for (let i = 0; i < 46; i++) { - dataSource.push({ - id: i, - name: `Edward King ${i}`, - age: 32, - address: `London, Park Lane no. ${i}`, - }); -} - -const columns = [ - { - title: '姓名', - dataIndex: 'name', - key: 'name', - }, - { - title: '年龄', - dataIndex: 'age', - key: 'age', - }, - { - title: '住址', - dataIndex: 'address', - key: 'address', - }, -]; +import get from 'lodash/get'; +import { components, fields2columns } from './SortableTable'; export interface SimpleTableProps { schema?: any; @@ -53,23 +26,46 @@ export function SimpleTable(props: SimpleTableProps) { associatedName, associatedKey, } = props; - const { fields, viewCollectionName, rowViewName, actions = [] } = schema; + const { rowKey = 'id', fields = [], rowViewName, actions = [] } = schema; const { sourceKey = 'id' } = activeTab.field||{}; const drawerRef = useRef(); const name = associatedName ? `${associatedName}.${resourceName}` : resourceName; - const { data } = useRequest(() => api.resource(name).list({ + const { data, mutate } = useRequest(() => api.resource(name).list({ associatedKey, })); console.log(activeTab); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const onChange = (selectedRowKeys: React.ReactText[]) => { + setSelectedRowKeys(selectedRowKeys); + } + const tableProps: any = {}; + if (actions.length) { + tableProps.rowSelection = { + selectedRowKeys, + onChange, + } + } return ( - - ({ - onClick: () => { - drawerRef.current.setVisible(true); - }, - })} columns={fields} /> + + ({ + onClick: () => { + drawerRef.current.setVisible(true); + drawerRef.current.getData(record[rowKey]); + } + })} + {...tableProps} + /> ); } diff --git a/packages/app/src/components/views/SortableTable/index.tsx b/packages/app/src/components/views/SortableTable/index.tsx new file mode 100644 index 000000000..b9b469cf7 --- /dev/null +++ b/packages/app/src/components/views/SortableTable/index.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +// @ts-ignore +import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc'; +import { MenuOutlined } from '@ant-design/icons'; +import arrayMove from 'array-move'; +import findIndex from 'lodash/findIndex'; +import get from 'lodash/get'; +import './style.less'; + +export const SortableItem = sortableElement(props => ); +export const SortableContainer = sortableContainer(props => ); + +export const DragHandle = sortableHandle(() => ( + +)); + +export const components = ({data, mutate}) => { + return { + body: { + wrapper: props => ( + { + if (oldIndex !== newIndex) { + const newData = arrayMove([].concat(data), oldIndex, newIndex).filter(el => !!el); + mutate(newData); + } + }} + {...props} + /> + ), + row: ({ className, style, ...restProps }) => { + // function findIndex base on Table rowKey props and should always be a right array index + const index = findIndex(data, (x: any) => x.id === restProps['data-row-key']); + return ; + }, + }, + }; +}; + +export function fields2columns(fields) { + const columns: any[] = fields.map(field => { + const type = get(field, 'component.type'); + if (type === 'sort') { + field.render = () => ; + } + return { + ...field, + ...(field.component||{}), + } + }); + return columns; +} diff --git a/packages/app/src/components/views/SortableTable/style.less b/packages/app/src/components/views/SortableTable/style.less new file mode 100644 index 000000000..7ee7be8ed --- /dev/null +++ b/packages/app/src/components/views/SortableTable/style.less @@ -0,0 +1,14 @@ +.row-dragging { + background: #fafafa; + border: 1px solid #ccc; + z-index: 99999; +} + +.row-dragging td { + padding: 16px; + visibility: hidden; +} + +.row-dragging .drag-visible { + visibility: visible; +} \ No newline at end of file diff --git a/packages/app/src/components/views/table.tsx b/packages/app/src/components/views/Table.tsx similarity index 50% rename from packages/app/src/components/views/table.tsx rename to packages/app/src/components/views/Table.tsx index e14789d9d..b944f7222 100644 --- a/packages/app/src/components/views/table.tsx +++ b/packages/app/src/components/views/Table.tsx @@ -1,9 +1,10 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Table as AntdTable, Card } from 'antd'; import { redirectTo } from '@/components/pages/CollectionLoader/utils'; import { Actions } from '@/components/actions'; import { request, useRequest } from 'umi'; import api from '@/api-client'; +import { components, fields2columns } from './SortableTable'; const columns = [ { @@ -40,28 +41,45 @@ export function Table(props: TableProps) { associatedName, associatedKey, } = props; - const { defaultTabId, fields, defaultTabName, rowKey = 'id', actions = [] } = schema; + const { fields, defaultTabName, rowKey = 'id', actions = [] } = schema; const name = associatedName ? `${associatedName}.${resourceName}` : resourceName; - const { data } = useRequest(() => api.resource(name).list({ + const { data, mutate } = useRequest(() => api.resource(name).list({ associatedKey, })); const { sourceKey = 'id' } = activeTab.field||{}; - console.log(data); - console.log(activeTab); + console.log(props); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const onChange = (selectedRowKeys: React.ReactText[]) => { + setSelectedRowKeys(selectedRowKeys); + } + const tableProps: any = {}; + if (actions.length) { + tableProps.rowSelection = { + selectedRowKeys, + onChange, + } + } return ( - ({ - onClick: () => { - redirectTo({ - ...props.match.params, - [activeTab ? 'newItem' : 'lastItem']: { - itemId: data[rowKey]||data.id, - tabName: defaultTabName, - }, - }); - }, - })} columns={fields} /> + ({ + onClick: () => { + redirectTo({ + ...props.match.params, + [activeTab ? 'newItem' : 'lastItem']: { + itemId: data[rowKey]||data.id, + tabName: defaultTabName, + }, + }); + }, + })} + {...tableProps} + /> ); } diff --git a/packages/app/src/components/views/details.tsx b/packages/app/src/components/views/details.tsx deleted file mode 100644 index 9b5b947b1..000000000 --- a/packages/app/src/components/views/details.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import { Card, Descriptions, Button } from 'antd'; -import { Actions } from '@/components/actions'; - -export function Details(props: any) { - console.log(props); - const { actions = [], fields = [] } = props.schema; - return ( - - - - Cloud Database - Prepaid - YES - 2018-04-24 18:00:00 - - 2019-04-24 18:00:00 - - $80.00 - $20.00 - $60.00 - - Data disk type: MongoDB -
- Database version: 3.4 -
- Package: dds.mongo.mid -
- Storage space: 10 GB -
- Replication factor: 3 -
- Region: East China 1
-
-
-
- ); -} diff --git a/packages/app/src/components/views/index.tsx b/packages/app/src/components/views/index.tsx index f389d3e8d..1fe38c68d 100644 --- a/packages/app/src/components/views/index.tsx +++ b/packages/app/src/components/views/index.tsx @@ -1,11 +1,12 @@ import React from 'react'; -import { Form, DrawerForm } from './Form/index'; -import { Table } from './Table'; -import { Details } from './Details'; -import { useRequest, request, Spin } from '@nocobase/client'; -import { SimpleTable } from './SimpleTable'; import api from '@/api-client'; +import { useRequest } from 'umi'; +import { Spin } from '@nocobase/client'; +import { SimpleTable } from './SimpleTable'; +import { Table } from './Table'; +import { Form, DrawerForm } from './Form/index'; +import { Details } from './Details'; const TEMPLATES = new Map(); @@ -24,19 +25,37 @@ registerView('Table', Table); registerView('SimpleTable', SimpleTable); registerView('Details', Details); -export default function ViewFactory(props) { - const { activeTab, viewCollectionName, viewName, reference } = props; - console.log({viewCollectionName, viewName}); - const { data = {}, error, loading, run } = useRequest(() => api.resource(viewCollectionName).getView({ - resourceKey: viewName, - }), { - refreshDeps: [viewCollectionName, viewName], +export interface ViewProps { + resourceName: string; + resourceKey?: string | number; + associatedName?: string; + associatedKey?: string | number; + viewName?: string; + [key: string]: any; +} + +export default function ViewFactory(props: ViewProps) { + const { + associatedName, + associatedKey, + resourceName, + viewName, + reference, + } = props; + const { data = {}, loading } = useRequest(() => { + const params = { + resourceKey: viewName, + associatedName: associatedName, + }; + return api.resource(resourceName).getView(params); + }, { + refreshDeps: [associatedName, resourceName, viewName], }); - console.log(activeTab); + console.log(data); if (loading) { return ; } - const { template } = data.data; + const { template } = data; const Template = getViewTemplate(template); - return Template &&