diff --git a/frontend/config/webpack.config.dev.js b/frontend/config/webpack.config.dev.js index 037270d9db..f6739e5e91 100644 --- a/frontend/config/webpack.config.dev.js +++ b/frontend/config/webpack.config.dev.js @@ -213,6 +213,11 @@ module.exports = { require.resolve('./polyfills'), require.resolve('react-dev-utils/webpackHotDevClient'), paths.appSrc + "/pages/search", + ], + appDTable: [ + require.resolve('./polyfills'), + require.resolve('react-dev-utils/webpackHotDevClient'), + paths.appSrc + "/app-dtable", ] }, diff --git a/frontend/config/webpack.config.prod.js b/frontend/config/webpack.config.prod.js index 3c5c7f590f..77cb530de1 100644 --- a/frontend/config/webpack.config.prod.js +++ b/frontend/config/webpack.config.prod.js @@ -90,7 +90,8 @@ module.exports = { sysAdmin: [require.resolve('./polyfills'), paths.appSrc + "/pages/sys-admin"], viewDataGrid: [require.resolve('./polyfills'), paths.appSrc + "/view-file-dtable.js"], viewCdoc: [require.resolve('./polyfills'), paths.appSrc + "/view-file-cdoc.js"], - search: [require.resolve('./polyfills'), paths.appSrc + "/pages/search"] + search: [require.resolve('./polyfills'), paths.appSrc + "/pages/search"], + appDTable: [require.resolve('./polyfills'), paths.appSrc + "/app-dtable"], }, output: { diff --git a/frontend/src/app-dtable.js b/frontend/src/app-dtable.js new file mode 100644 index 0000000000..4b1c1775b3 --- /dev/null +++ b/frontend/src/app-dtable.js @@ -0,0 +1,46 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import SidePanel from './pages/dtable/side-panel'; +import MainPanel from './pages/dtable/main-panel'; + +import './css/layout.css'; +import './css/side-panel.css'; +import './css/dtable.css'; + +class AppDTable extends React.Component { + + constructor(props) { + super(props); + this.state = { + currentTab: 'dtable', + } + } + + componentDidMount() { + const seletedTabs = ['apps', 'templetes']; + let paths = location.href.split('/'); + let tab = paths[paths.indexOf('dtable') + 1]; + let currentTab = seletedTabs.indexOf(tab) > -1 ? tab : 'dtable'; + this.setState({currentTab: currentTab}); + } + + onTabClick = (tab) => { + if (tab !== this.state.currentTab) { + this.setState({currentTab: tab}); + } + } + + render() { + return ( +
+ + +
+ ); + } +} + +ReactDOM.render( + , + document.getElementById('wrapper') +); diff --git a/frontend/src/app.js b/frontend/src/app.js index fe2418374f..78abf153af 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -12,7 +12,6 @@ import DraftsView from './pages/drafts/drafts-view'; import DraftContent from './pages/drafts/draft-content'; import FilesActivities from './pages/dashboard/files-activities'; import Starred from './pages/starred/starred'; -import DTable from './pages/dtable/dtable'; import LinkedDevices from './pages/linked-devices/linked-devices'; import editUtilties from './utils/editor-utilties'; import ShareAdminLibraries from './pages/share-admin/libraries'; @@ -247,7 +246,6 @@ class App extends Component { /> - diff --git a/frontend/src/components/main-side-nav.js b/frontend/src/components/main-side-nav.js index 42ce0ff4a3..4dfea55b9d 100644 --- a/frontend/src/components/main-side-nav.js +++ b/frontend/src/components/main-side-nav.js @@ -77,6 +77,11 @@ class MainSideNav extends React.Component { this.props.tabItemClick(param, id); } + onDTableClick = () => { + let url = siteRoot + 'dtable/' + window.open(url); + } + getActiveClass = (tab) => { return this.props.currentTab === tab ? 'active' : ''; } @@ -198,15 +203,6 @@ class MainSideNav extends React.Component { -

{gettext('Database')}

-
    -
  • - this.tabItemClick(e, 'dtable')}> - - DTable - -
  • -

{gettext('Tools')}

    @@ -267,6 +263,12 @@ class MainSideNav extends React.Component {
+ +
+ + Database + +
); } diff --git a/frontend/src/css/dtable-page.css b/frontend/src/css/dtable.css similarity index 60% rename from frontend/src/css/dtable-page.css rename to frontend/src/css/dtable.css index 4ba634ba72..ec0c54fbcd 100644 --- a/frontend/src/css/dtable-page.css +++ b/frontend/src/css/dtable.css @@ -1,3 +1,89 @@ +.side-panel-north, .main-panel-north { + background-color: #fff; + box-shadow: 0 2px 3px #eee; +} + +.dtable-header { + height: 50px; +} + +.dtable-header .dtable-logo { + padding-right: 6px; + font-size: 36px; + line-height: 36px; + color: #FEAC74; +} + +.dtable-header .dtable-text { + display: flex; + align-items: center; + font-size: 1.25rem; + color: #555555; +} + +.dtable-side-nav { + padding: 12px; +} + +.dtable-nav-title { + font-size: 1rem; + color: #322; + font-weight: normal; + line-height: 1.5; +} + +.dtable-side-nav .dtable-nav-list { + flex-wrap: inherit; + height: auto; + border-bottom: none; +} + +.dtable-nav-list .dtable-nav-item { + line-height: 38px; + border-radius: 0.25rem; +} + +.dtable-nav-list .dtable-nav-item .dtable-nav-link { + margin: 0; + width: 100%; + color: #000; +} + +.dtable-nav-list .dtable-nav-item .dtable-nav-link:hover { + background-color: #f5f5f5; +} + +.dtable-nav-list .dtable-nav-item.active .dtable-nav-link { + color: #fff; + background-color: #F4A74C; +} + +.dtable-nav-list .dtable-nav-link .nav-icon { + padding-right: 0.5rem; + font-size: 1.5rem; +} + +.dtable-header .common-toolbar { + display: flex; + margin-left: auto; +} + +.dtable-header .common-toolbar>div { + padding-right: 1rem; + cursor: pointer; +} + +.dtable-center .cur-view-title { + line-height: 50px; + font-size: 1.5rem; + padding-left: 12px; + border-bottom: 1px solid #eee; +} + +.dtable-center .dtable-add-btn { + width: 8rem; +} + .workspace { margin: 0.5rem 0; } @@ -109,4 +195,7 @@ } .table-item-more-operation i:hover { color: #666; -} \ No newline at end of file +} + + + diff --git a/frontend/src/pages/dtable/list-table-item.js b/frontend/src/pages/dtable/dtable-item-common.js similarity index 51% rename from frontend/src/pages/dtable/list-table-item.js rename to frontend/src/pages/dtable/dtable-item-common.js index e8229607ab..32508c6ba6 100644 --- a/frontend/src/pages/dtable/list-table-item.js +++ b/frontend/src/pages/dtable/dtable-item-common.js @@ -1,81 +1,61 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; -import { gettext, siteRoot } from '../../utils/constants'; -import DeleteTableDialog from '../../components/dialog/delete-table-dialog'; -import ShareTableDialog from '../../components/dialog/share-table-dialog'; import Rename from '../../components/rename'; +const gettext = window.gettext; +const siteRoot = window.app.config.siteRoot; + const propTypes = { - table: PropTypes.object.isRequired, - workspaceID: PropTypes.number.isRequired, - renameTable: PropTypes.func.isRequired, - deleteTable: PropTypes.func.isRequired, - onUnfreezedItem: PropTypes.func.isRequired, - onFreezedItem: PropTypes.func.isRequired, isItemFreezed: PropTypes.bool.isRequired, + table: PropTypes.object.isRequired, + renameTable: PropTypes.func.isRequired, + onDeleteTableToggle: PropTypes.func.isRequired, + onShareTableToggle: PropTypes.func.isRequired, + onFreezedItem: PropTypes.func.isRequired, + onUnfreezedItem: PropTypes.func.isRequired, }; -class ListTableItem extends React.Component { +class DTableItemCommon extends React.Component { constructor(props) { super(props); this.state = { isTableRenaming: false, - isTableDeleting: false, - isTableSharing: false, dropdownOpen: false, active: false, }; } - componentWillReceiveProps(nextProps) { - if (!nextProps.isItemFreezed) { - this.setState({ active: false }); - } - } - onMouseEnter = () => { if (!this.props.isItemFreezed) { - this.setState({ - active: true - }); + this.setState({active: true}); } } onMouseLeave = () => { if (!this.props.isItemFreezed) { - this.setState({ - active: false - }); + this.setState({active: false}); } } - - onRenameTableCancel = () => { + + onRenameTableConfirm = (newTableName) => { + let oldTableName = this.props.table.name; + this.props.renameTable(oldTableName, newTableName); + this.onRenameTableToggle(); + } + + onRenameTableToggle = () => { this.setState({isTableRenaming: !this.state.isTableRenaming}); this.props.onUnfreezedItem(); } - onRenameTableConfirm = (newTableName) => { - let oldTableName = this.props.table.name; - this.props.renameTable(oldTableName, newTableName); - this.onRenameTableCancel(); + onDeleteTableToggle = () => { + this.props.onDeleteTableToggle(this.props.table); } - onDeleteTableCancel = () => { - this.setState({isTableDeleting: !this.state.isTableDeleting}); - this.props.onUnfreezedItem(); - } - - onDeleteTableSubmit = () => { - let tableName = this.props.table.name; - this.props.deleteTable(tableName); - this.onDeleteTableCancel(); - } - - onShareTableCancel = () => { - this.setState({isTableSharing: !this.state.isTableSharing}); - this.props.onUnfreezedItem(); + onShareTableToggle = () => { + this.props.onShareTableToggle(this.props.table); } dropdownToggle = () => { @@ -90,26 +70,24 @@ class ListTableItem extends React.Component { render() { let table = this.props.table; - let tableHref = siteRoot + 'workspace/' + this.props.workspaceID + '/dtable/' + table.name + '/'; + let tableHref = siteRoot + 'workspace/' + table.workspace_id + '/dtable/' + table.name + '/'; return ( -
+
- {this.state.isTableRenaming && + {this.state.isTableRenaming && ( - } - {!this.state.isTableRenaming && - {table.name} - } + )} + {!this.state.isTableRenaming && {table.name}}
- {this.state.active && + {this.state.active && ( - {gettext('Rename')} - {gettext('Delete')} - {gettext('Share')} + {gettext('Rename')} + {gettext('Delete')} + {gettext('Share')} - } - {this.state.isTableDeleting && - - } - {this.state.isTableSharing && - - } + )}
); } } -ListTableItem.propTypes = propTypes; +DTableItemCommon.propTypes = propTypes; -export default ListTableItem; +export default DTableItemCommon; diff --git a/frontend/src/pages/dtable/share-table-item.js b/frontend/src/pages/dtable/dtable-item-shared.js similarity index 75% rename from frontend/src/pages/dtable/share-table-item.js rename to frontend/src/pages/dtable/dtable-item-shared.js index 9696abe0db..4bd111a7e8 100644 --- a/frontend/src/pages/dtable/share-table-item.js +++ b/frontend/src/pages/dtable/dtable-item-shared.js @@ -1,15 +1,15 @@ -import React, {Component} from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; -import {gettext, siteRoot} from '../../utils/constants'; -import '../../css/dtable-page.css'; +const gettext = window.gettext; +const { siteRoot } = window.app.config; -const shareTableItemPropTypes = { +const propTypes = { table: PropTypes.object.isRequired, leaveShareTable: PropTypes.func.isRequired, }; -class ShareTableItem extends Component { +class DTableItemShared extends React.Component { constructor(props) { super(props); @@ -18,23 +18,20 @@ class ShareTableItem extends Component { }; } + + onMouseEnter = () => { + this.setState({active: true}); + }; + + onMouseLeave = () => { + this.setState({active: false}); + }; + onLeaveShareTableSubmit = () => { let table = this.props.table; this.props.leaveShareTable(table); }; - - onMouseEnter = () => { - this.setState({ - active: true - }); - }; - - onMouseLeave = () => { - this.setState({ - active: false - }); - }; - + render() { let table = this.props.table; let tableHref = siteRoot + 'workspace/' + table.workspace_id + '/dtable/' + table.name + '/'; @@ -59,6 +56,6 @@ class ShareTableItem extends Component { } } -ShareTableItem.propTypes = shareTableItemPropTypes; +DTableItemShared.propTypes = propTypes; -export default ShareTableItem; \ No newline at end of file +export default DTableItemShared; diff --git a/frontend/src/pages/dtable/dtable-workspace-common.js b/frontend/src/pages/dtable/dtable-workspace-common.js new file mode 100644 index 0000000000..15c5694b12 --- /dev/null +++ b/frontend/src/pages/dtable/dtable-workspace-common.js @@ -0,0 +1,192 @@ +import React, { Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { seafileAPI } from '../../utils/seafile-api'; +import Loading from '../../components/loading'; +import DTableItemCommon from './dtable-item-common'; +import CreateTableDialog from '../../components/dialog/create-table-dialog'; +import DeleteTableDialog from '../../components/dialog/delete-table-dialog'; +import ShareTableDialog from '../../components/dialog/share-table-dialog'; + +const gettext = window.gettext; + +const propTypes = { + workspace: PropTypes.object.isRequired, +}; + +class DTableWorkspaceCommon extends React.Component { + + constructor(props) { + super(props); + this.state = { + tableList: [], + isDataLoading: true, + isItemFreezed: false, + isShowCreateDialog: false, + isShowDeleteDialog: false, + isShowSharedDialog: false, + currentTable: null, + } + } + + componentDidMount() { + let workspace = this.props.workspace; + let tableList = workspace.table_list; + this.setState({ + tableList: tableList, + isDataLoading: false, + }); + } + + componentWillReceiveProps(nextProps) { + if (nextProps.workspace !== this.props.workspace) { + let workspace = nextProps.workspace; + let tableList = workspace.table_list; + this.setState({tableList: tableList}); + } + } + + onFreezedItem = () => { + this.setState({isItemFreezed: true}); + } + + onUnfreezedItem = () => { + this.setState({isItemFreezed: false}); + } + + onCreateTableToggle = () => { + this.setState({isShowCreateDialog: !this.state.isShowCreateDialog}); + } + + onDeleteTableToggle = (table) => { + this.setState({ + isShowDeleteDialog: !this.state.isShowDeleteDialog, + currentTable: table + }); + this.onUnfreezedItem(); + } + + onDeleteTableSubmit = () => { + let tableName = this.state.currentTable.name; + this.deleteTable(tableName); + this.onDeleteTableToggle(); + } + + onShareTableToggle = (table) => { + this.setState({ + isShowSharedDialog: !this.state.isShowSharedDialog, + currentTable: table + }); + this.onUnfreezedItem(); + } + + onCreateTable = (tableName, owner) => { + seafileAPI.createTable(tableName, owner).then((res) => { + this.state.tableList.push(res.data.table); + this.setState({ + tableList: this.state.tableList + }); + }).catch((error) => { + if(error.response) { + this.setState({errorMsg: gettext('Error')}); + } + }); + + this.onCreateTableToggle(); + } + + deleteTable = (tableName) => { + let workspaceID = this.props.workspace.id; + seafileAPI.deleteTable(workspaceID, tableName).then(() => { + let tableList = this.state.tableList.filter(table => { + return table.name !== tableName; + }); + this.setState({tableList: tableList}); + }).catch((error) => { + if(error.response) { + this.setState({errorMsg: gettext('Error')}); + } + }); + } + + renameTable = (oldTableName, newTableName) => { + let workspaceID = this.props.workspace.id; + seafileAPI.renameTable(workspaceID, oldTableName, newTableName).then((res) => { + let tableList = this.state.tableList.map((table) => { + if (table.name === oldTableName) { + table = res.data.table; + } + return table; + }); + this.setState({tableList: tableList}); + }).catch((error) => { + if(error.response) { + this.setState({errorMsg: gettext('Error')}); + } + }); + } + + render() { + let { isDataLoading } = this.state; + if (isDataLoading) { + return + } + + let { workspace } = this.props; + let { tableList, isItemFreezed } = this.state; + let isPersonal = workspace.owner_type === 'Personal'; + + return ( + +
+
{isPersonal ? gettext('My Tables') : workspace.owner_name}
+
+ {tableList.map((table, index) => { + return ( + + ); + })} + +
+
+ {this.state.isShowCreateDialog && ( + + )} + {this.state.isShowDeleteDialog && ( + + )} + {this.state.isShowSharedDialog && + + } +
+ ); + } +} + + +DTableWorkspaceCommon.propTypes = propTypes; + +export default DTableWorkspaceCommon; diff --git a/frontend/src/pages/dtable/dtable-workspace-shared.js b/frontend/src/pages/dtable/dtable-workspace-shared.js new file mode 100644 index 0000000000..3965c65246 --- /dev/null +++ b/frontend/src/pages/dtable/dtable-workspace-shared.js @@ -0,0 +1,78 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import DTableItemShared from './dtable-item-shared'; +import Loading from '../../components/loading'; +import { seafileAPI } from '../../utils/seafile-api'; + +const gettext = window.gettext; +const username = window.app.pageOptions.username; + +const propTypes = { + tableList: PropTypes.array.isRequired, +}; + +class DTableWorkspaceShared extends React.Component { + + constructor(props) { + super(props); + this.state = { + isDataLoading: true, + tableList: [], + }; + } + + componentDidMount() { + let tableList = this.props.tableList; + this.setState({ + isDataLoading: false, + tableList: tableList, + }); + } + + leaveShareTable = (table) => { + let email = username; + let tableName = table.name; + let workspaceID = table.workspace_id; + seafileAPI.deleteTableShare(workspaceID, tableName, email).then(() => { + let tableList = this.state.tableList.filter(table => { + return table.name !== tableName; + }); + this.setState({tableList: tableList}); + }).catch((error) => { + if(error.response) { + this.setState({errorMsg: gettext('Error')}); + } else { + this.setState({ + errorMsg: gettext('Please check the network.') + }); + } + }); + } + + render() { + if (this.state.isDataLoading) { + return + } + + if (!this.state.tableList.length) { + return ""; + } + + return ( +
+
{gettext('Shared with me')}
+
+ {this.state.tableList.map((table, index) => { + return ( + + ); + })} +
+
+ ); + } +} + +DTableWorkspaceShared.propTypes = propTypes; + +export default DTableWorkspaceShared; diff --git a/frontend/src/pages/dtable/dtable.js b/frontend/src/pages/dtable/dtable.js deleted file mode 100644 index 8a0e8e60d7..0000000000 --- a/frontend/src/pages/dtable/dtable.js +++ /dev/null @@ -1,228 +0,0 @@ -import React, { Component, Fragment } from 'react'; -import PropTypes from 'prop-types'; -import MediaQuery from 'react-responsive'; -import { Button } from 'reactstrap'; -import { seafileAPI } from '../../utils/seafile-api'; -import { gettext, username } from '../../utils/constants'; -import CommonToolbar from '../../components/toolbar/common-toolbar'; -import Loading from '../../components/loading'; -import CreateTableDialog from '../../components/dialog/create-table-dialog'; -import ShareTableItem from './share-table-item'; -import Workspace from './workspace'; - -import '../../css/dtable-page.css'; - -const propTypes = { - onShowSidePanel: PropTypes.func.isRequired, - onSearchedClick: PropTypes.func.isRequired, -}; - -class DTable extends Component { - - constructor(props) { - super(props); - this.state = { - loading: true, - shareTableLoading: true, - errorMsg: '', - workspaceList: [], - shareTableList: [], - isShowAddDTableDialog: false, - currentWorkspace: null, - }; - } - - componentDidMount() { - this.listWorkspaces(); - this.listSharedTables(); - } - - onAddDTable = () => { - this.setState({ isShowAddDTableDialog: !this.state.isShowAddDTableDialog }); - } - - newDtable = () => { - if (this.state.currentWorkspace) { - this.setState({ currentWorkspace: null }); - } - this.onAddDTable(); - } - - setCurrentWorkspace = (currentWorkspace) => { - this.setState({ currentWorkspace: currentWorkspace }); - } - - createDTable = (tableName, owner) => { - seafileAPI.createTable(tableName, owner).then(() => { - this.listWorkspaces(); - }).catch((error) => { - if(error.response) { - this.setState({errorMsg: gettext('Error')}); - } - }); - this.onAddDTable(); - } - - listWorkspaces = () => { - seafileAPI.listWorkspaces().then((res) => { - this.setState({ - loading: false, - workspaceList: res.data.workspace_list, - }); - }).catch((error) => { - if (error.response) { - this.setState({ - loading: false, - errorMsg: gettext('Error') - }); - } else { - this.setState({ - loading: false, - errorMsg: gettext('Please check the network.') - }); - } - }); - } - - listSharedTables = () => { - seafileAPI.listSharedTables().then((res) => { - this.setState({ - shareTableLoading: false, - shareTableList: res.data.table_list, - }); - }).catch((error) => { - if (error.response) { - this.setState({ - shareTableLoading: false, - errorMsg: gettext('Error') - }); - } else { - this.setState({ - shareTableLoading: false, - errorMsg: gettext('Please check the network.') - }); - } - }); - } - - leaveShareTable = (table) => { - let email = username; - let tableName = table.name; - let workspaceID = table.workspace_id; - seafileAPI.deleteTableShare(workspaceID, tableName, email).then(() => { - let shareTableList = this.state.shareTableList.filter(table => { - return table.name !== tableName; - }); - this.setState({shareTableList: shareTableList}); - }).catch((error) => { - if(error.response) { - this.setState({errorMsg: gettext('Error')}); - } else { - this.setState({ - errorMsg: gettext('Please check the network.') - }); - } - }); - } - - renderShareTablePanel = () => { - return ( -
-
{gettext('Shared with me')}
-
- {this.state.shareTableList.map((table, index) => { - return ( - - ); - })} -
-
- ); - } - - render() { - const { workspaceList, loading } = this.state; - - let personalWorkspaceList = workspaceList.filter(workspace => { - return workspace.owner_type === 'Personal'; - }); - let personalWorkspaceMessage = personalWorkspaceList[0]; - let groupWorkspaceList = workspaceList.filter(workspace => { - return workspace.owner_type === 'Group'; - }); - - if (loading) { - return(); - } - - return ( - -
-
- -
- - - - - - - - -
-
- -
-
-
-
-

DTable

-
-
- {this.state.errorMsg && -

{this.state.errorMsg}

- } - - {(!this.state.shareTableLoading && this.state.shareTableList.length > 0) && - this.renderShareTablePanel() - } - { - groupWorkspaceList.map((workspace, index) => { - return ( - - ); - }) - } - {this.state.isShowAddDTableDialog && -
- -
- } -
-
-
-
- ); - } -} - -DTable.propTypes = propTypes; - -export default DTable; diff --git a/frontend/src/pages/dtable/main-panel-apps.js b/frontend/src/pages/dtable/main-panel-apps.js new file mode 100644 index 0000000000..1173bdc7f5 --- /dev/null +++ b/frontend/src/pages/dtable/main-panel-apps.js @@ -0,0 +1,19 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const propTypes = { + +}; + +class MainPanelApps extends React.Component { + + render() { + return ( +
apps
+ ); + } +} + +MainPanelApps.propTypes = propTypes; + +export default MainPanelApps; diff --git a/frontend/src/pages/dtable/main-panel-dtables.js b/frontend/src/pages/dtable/main-panel-dtables.js new file mode 100644 index 0000000000..2e266841d6 --- /dev/null +++ b/frontend/src/pages/dtable/main-panel-dtables.js @@ -0,0 +1,139 @@ +import React, { Fragment } from 'react'; +import PropTypes from 'prop-types'; +import Loading from '../../components/loading'; +import { gettext } from '../../utils/constants'; +import { seafileAPI } from '../../utils/seafile-api'; +import Workspace from './model/workspace'; +import DTableWorkspaceCommon from './dtable-workspace-common'; +import DTableWorkspaceShared from './dtable-workspace-shared'; +import CreateTableDialog from '../../components/dialog/create-table-dialog'; + +const propTypes = { + +}; + +class MainPanelDTables extends React.Component { + + constructor(props) { + super(props); + this.state = { + errorMsg: null, + workspaceList: [], + sharedTableList: [], + isWorkspaceListLoading: true, + isSharedTableListLoading: true, + isShowCreateDialog: false + } + } + + componentDidMount() { + this.loadWorkspaceList(); + this.loadSharedTableList(); + } + + loadWorkspaceList = () => { + seafileAPI.listWorkspaces().then((res) => { + let workspaceList = res.data.workspace_list.map(item => { + return new Workspace(item); + }); + this.setState({ + isWorkspaceListLoading: false, + workspaceList: workspaceList, + }); + }).catch((error) => { + this.errorCallbackHandle(error); + }); + } + + loadSharedTableList = () => { + seafileAPI.listSharedTables().then((res) => { + this.setState({ + isSharedTableListLoading: false, + sharedTableList: res.data.table_list, + }); + }).catch((error) => { + this.errorCallbackHandle(error); + }); + } + + errorCallbackHandle = (error) => { + if (error.response) { + this.setState({ + loading: false, + errorMsg: gettext('Error') + }); + } else { + this.setState({ + loading: false, + errorMsg: gettext('Please check the network.') + }); + } + } + + onCreateTableToggle = () => { + this.setState({isShowCreateDialog: !this.state.isShowCreateDialog}); + } + + onCreateTable = (tableName, owner) => { + seafileAPI.createTable(tableName, owner).then(() => { + this.loadWorkspaceList(); + }).catch((error) => { + if(error.response) { + this.setState({errorMsg: gettext('Error')}); + } + }); + + this.onCreateTableToggle(); + } + + render() { + let { isWorkspaceListLoading, isSharedTableListLoading } = this.state; + if (isWorkspaceListLoading || isSharedTableListLoading) { + return + } + + let { workspaceList, sharedTableList } = this.state; + let personalWorkspace = workspaceList.find(workspace => { + return workspace.owner_type === 'Personal'; + }); + + let groupWorkspaceList = workspaceList.filter(workspace => { + return workspace.owner_type === 'Group'; + }); + + return ( + +
+
+
DTable
+
+ {this.state.errorMsg && +

{this.state.errorMsg}

+ } + {!this.state.errorMsg && ( + + + + {groupWorkspaceList.length > 0 && groupWorkspaceList.map((workspace, index) => { + return () + })} + + + )} +
+
+
+ {this.state.isShowCreateDialog && ( + + )} +
+ ); + } +} + +MainPanelDTables.propTypes = propTypes; + +export default MainPanelDTables; diff --git a/frontend/src/pages/dtable/main-panel-templetes.js b/frontend/src/pages/dtable/main-panel-templetes.js new file mode 100644 index 0000000000..daf60f505b --- /dev/null +++ b/frontend/src/pages/dtable/main-panel-templetes.js @@ -0,0 +1,19 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const propTypes = { + +}; + +class MainPanelTempletes extends React.Component { + + render() { + return ( +
templetes
+ ); + } +} + +MainPanelTempletes.propTypes = propTypes; + +export default MainPanelTempletes; diff --git a/frontend/src/pages/dtable/main-panel.js b/frontend/src/pages/dtable/main-panel.js new file mode 100644 index 0000000000..18f333b4ef --- /dev/null +++ b/frontend/src/pages/dtable/main-panel.js @@ -0,0 +1,40 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Router } from '@reach/router' +import MainPanelDTables from './main-panel-dtables'; +import MainPanelApps from './main-panel-apps'; +import MainPanelTempletes from './main-panel-templetes'; + + +const siteRoot = window.app.config.siteRoot; + + +const propTypes = { + +}; + +class MainPanel extends React.Component { + + render() { + return ( +
+
+
+
Search
+
Notification
+
+
+
+ + + + + +
+ ); + } +} + +MainPanel.propTypes = propTypes; + +export default MainPanel; diff --git a/frontend/src/pages/dtable/model/workspace.js b/frontend/src/pages/dtable/model/workspace.js new file mode 100644 index 0000000000..84360665e2 --- /dev/null +++ b/frontend/src/pages/dtable/model/workspace.js @@ -0,0 +1,12 @@ +class Workspace { + + constructor(obj) { + this.id = obj.id || ''; + this.owner_name = obj.owner_name || ''; + this.owner_type = obj.owner_type || ''; + this.table_list = obj.table_list || []; + } + +} + +export default Workspace; \ No newline at end of file diff --git a/frontend/src/pages/dtable/side-panel.js b/frontend/src/pages/dtable/side-panel.js new file mode 100644 index 0000000000..a79a9090be --- /dev/null +++ b/frontend/src/pages/dtable/side-panel.js @@ -0,0 +1,63 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Link } from '@reach/router'; + +const gettext = window.gettext; +const siteRoot = window.app.config.siteRoot; + +const propTypes = { + currentTab: PropTypes.string.isRequired, + onTabClick: PropTypes.func.isRequired, +}; + +class SidePanel extends React.Component { + + onTabClick = (tab) => { + this.props.onTabClick(tab); + } + + getActiveClass = (tab) => { + return this.props.currentTab === tab ? 'active' : ''; + } + + render() { + + return ( +
+
+ + DTable +
+
+
+

{gettext('Main')}

+
    +
  • + + + {gettext('DTable')} + +
  • +
  • + + + {gettext('Apps')} + +
  • +
  • + + + {gettext('Templetes')} + +
  • +
+
+
+
+ ); + } +} + +SidePanel.propTypes = propTypes; + +export default SidePanel; diff --git a/frontend/src/pages/dtable/workspace.js b/frontend/src/pages/dtable/workspace.js deleted file mode 100644 index 886846575f..0000000000 --- a/frontend/src/pages/dtable/workspace.js +++ /dev/null @@ -1,122 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { seafileAPI } from '../../utils/seafile-api'; -import { gettext } from '../../utils/constants'; -import ListTableItem from './list-table-item'; - -const propTypes = { - workspace: PropTypes.object.isRequired, - setCurrentWorkspace: PropTypes.func.isRequired, - onAddDTable: PropTypes.func.isRequired, -}; - -class Workspace extends React.Component { - - constructor(props) { - super(props); - this.state = { - tableList: props.workspace.table_list, - errorMsg: '', - isItemFreezed: false, - }; - } - - componentWillReceiveProps(nextProps) { - if (nextProps.workspace.table_list !== this.props.workspace.table_list) { - this.setState({ - tableList: nextProps.workspace.table_list - }); - } - } - - onFreezedItem = () => { - this.setState({isItemFreezed: true}); - } - - onUnfreezedItem = () => { - this.setState({isItemFreezed: false}); - } - - renameTable = (oldTableName, newTableName) => { - let workspaceID = this.props.workspace.id; - seafileAPI.renameTable(workspaceID, oldTableName, newTableName).then((res) => { - let tableList = this.state.tableList.map((table) => { - if (table.name === oldTableName) { - table = res.data.table; - } - return table; - }); - this.setState({tableList: tableList}); - }).catch((error) => { - if(error.response) { - this.setState({errorMsg: gettext('Error')}); - } - }); - } - - deleteTable = (tableName) => { - let workspaceID = this.props.workspace.id; - seafileAPI.deleteTable(workspaceID, tableName).then(() => { - let tableList = this.state.tableList.filter(table => { - return table.name !== tableName; - }); - this.setState({tableList: tableList}); - }).catch((error) => { - if(error.response) { - this.setState({errorMsg: gettext('Error')}); - } - }); - } - - addTable = (e) => { - e.preventDefault(); - this.props.setCurrentWorkspace(this.props.workspace); - this.props.onAddDTable(); - } - - render() { - let workspace = this.props.workspace; - let isPersonal = workspace.owner_type === 'Personal'; - let { tableList, isItemFreezed } = this.state; - - return( -
-
0 ? '' : 'table-heading-border'}`}>{isPersonal ? gettext('My Tables') : workspace.owner_name}
- {tableList.length > 0 ? -
- {tableList.map((table, index) => { - return ( - - ); - })} - -
- : - - - - -
{gettext('No Tables')}
- } -
- ); - } - -} - -Workspace.propTypes = propTypes; - -export default Workspace; \ No newline at end of file diff --git a/media/css/seahub_react.css b/media/css/seahub_react.css index 5afb8190de..86d8b55251 100644 --- a/media/css/seahub_react.css +++ b/media/css/seahub_react.css @@ -579,6 +579,37 @@ ul,ol,li { padding:12px; } +.side-nav-link { + display: flex; + margin: 24px 12px; + color: #FFFFFF; + border-radius: 0.25rem; + background-color: #FEAC74; +} + +.side-nav-link:hover { + background-color: #F3985A; + cursor: pointer; +} + +.side-nav-link .link-icon { + padding: 0 0.5rem; + font-size: 1.5rem; +} + +.side-nav-link .link-icon.icon-right { + display: flex; + align-items: center; + font-size: 1rem; +} + +.side-nav-link .link-text { + display: flex; + flex: 1; + align-items: center; + padding-left: 0.25rem; +} + .side-nav-footer { display:flex; flex-shrink:0; diff --git a/seahub/templates/react_dtable.html b/seahub/templates/react_dtable.html new file mode 100644 index 0000000000..677c146db2 --- /dev/null +++ b/seahub/templates/react_dtable.html @@ -0,0 +1,55 @@ +{% load seahub_tags i18n staticfiles %} +{% load render_bundle from webpack_loader %} + + + + +DTable + + + + + + + + + + +{% render_bundle 'appDTable' 'css' %} +{% if branding_css != '' %}{% endif %} +{% if enable_branding_css %}{% endif %} + + + +
+ + + + +{% render_bundle 'commons' %} +{% render_bundle 'appDTable' 'js' %} + + + diff --git a/seahub/urls.py b/seahub/urls.py index 85470677bd..ba2761557f 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -237,7 +237,9 @@ urlpatterns = [ url(r'^my-libs/deleted/$', react_fake_view, name="my_libs_deleted"), url(r'^org/$', react_fake_view, name="org"), url(r'^invitations/$', react_fake_view, name="invitations"), - url(r'^dtable/$', react_fake_view, name='dtable'), + url(r'^dtable/$', dtable_fake_view, name='dtable'), + url(r'^dtable/apps/$', dtable_fake_view, name='dtable'), + url(r'^dtable/templetes/$', dtable_fake_view, name='dtable'), ### Ajax ### url(r'^ajax/repo/(?P[-0-9a-f]{36})/dirents/$', get_dirents, name="get_dirents"), diff --git a/seahub/views/__init__.py b/seahub/views/__init__.py index d1e76a2723..25b347fc48 100644 --- a/seahub/views/__init__.py +++ b/seahub/views/__init__.py @@ -1269,3 +1269,7 @@ def react_fake_view(request, **kwargs): 'folder_perm_enabled': folder_perm_enabled, 'file_audit_enabled' : FILE_AUDIT_ENABLED }) + +@login_required +def dtable_fake_view(request, **kwargs): + return render(request, 'react_dtable.html')