From 1a639e539f3e610ff7bd81fc25df4ddbc3a2d5ca Mon Sep 17 00:00:00 2001 From: Thomas Rueckstiess Date: Mon, 31 Oct 2016 16:07:07 +1100 Subject: [PATCH 1/7] [wip] handle all main routing internally in ./src/app/home. --- src/app/home/collection.js | 10 ++- src/app/home/index.js | 88 ++++++++++++------- src/internal-packages/database/index.js | 19 ++++ .../database/lib/components/index.jsx | 13 +++ src/internal-packages/database/package.json | 9 ++ .../explain/lib/stores/index.js | 8 +- .../schema/lib/store/index.jsx | 10 ++- .../lib/components/sidebar-database.jsx | 5 +- .../sidebar-instance-properties.jsx | 7 +- .../validation/lib/stores/index.js | 7 +- 10 files changed, 124 insertions(+), 52 deletions(-) create mode 100644 src/internal-packages/database/index.js create mode 100644 src/internal-packages/database/lib/components/index.jsx create mode 100644 src/internal-packages/database/package.json diff --git a/src/app/home/collection.js b/src/app/home/collection.js index 01b1de852ff..f06e5eee129 100644 --- a/src/app/home/collection.js +++ b/src/app/home/collection.js @@ -5,6 +5,7 @@ var MongoDBCollection = require('../models/mongodb-collection'); var React = require('react'); var ReactDOM = require('react-dom'); var NamespaceStore = require('hadron-reflux-store').NamespaceStore; +var toNS = require('mongodb-ns'); var _ = require('lodash'); var app = require('ampersand-app'); @@ -194,9 +195,12 @@ var MongoDBCollectionView = View.extend({ this.schemaActions.resizeMiniCharts(); } }, - onCollectionChanged: function() { - this.ns = NamespaceStore.ns; - if (!this.ns) { + onCollectionChanged: function(ns) { + if (ns === this.ns) { + return; + } + this.ns = ns; + if (!ns || !toNS(ns || '').collection) { this.visible = false; debug('No active collection namespace so no schema has been requested yet.'); return; diff --git a/src/app/home/index.js b/src/app/home/index.js index 0566f2b8dd0..88e4afbb29e 100644 --- a/src/app/home/index.js +++ b/src/app/home/index.js @@ -1,5 +1,5 @@ var View = require('ampersand-view'); -var format = require('util').format; +// var format = require('util').format; // var IdentifyView = require('../identify'); var CollectionView = require('./collection'); var { NamespaceStore } = require('hadron-reflux-store'); @@ -55,9 +55,10 @@ var HomeView = View.extend({ * TODO (imlucas) Handle state when rtss permissions not available. */ this.serverStatsView = app.appRegistry.getComponent('RTSS.ServerStats'); + this.collectionsTable = app.appRegistry.getComponent('Database.CollectionsTable'); this.listenTo(app.instance, 'sync', this.onInstanceFetched); this.listenTo(app.connection, 'change:name', this.updateTitle); - NamespaceStore.listen(this.onNamespaceChange.bind(this)); + NamespaceStore.listen(this.switchMainContent.bind(this)); ipc.on('window:show-compass-tour', this.showTour.bind(this, true)); ipc.on('window:show-network-optin', this.showOptIn.bind(this)); @@ -68,15 +69,6 @@ var HomeView = View.extend({ }, render: function() { this.renderWithTemplate(this); - var containerNode = this.queryByHook('report-zero-state'); - ReactDOM.render( - React.createElement(this.serverStatsView, { interval: 1000 }), - containerNode - ); - NamespaceStore.listen(() => { - ReactDOM.unmountComponentAtNode(containerNode); - }); - const SideBarComponent = app.appRegistry.getComponent('Sidebar.Component'); ReactDOM.render( React.createElement(SideBarComponent), @@ -89,6 +81,33 @@ var HomeView = View.extend({ this.tourClosed(); } }, + switchMainContent: function(namespace) { + if (namespace === this.ns) { + debug('already selected namespace', namespace); + return; + } + this.ns = namespace; + const ns = toNS(namespace); + var containerNode = this.queryByHook('report-zero-state'); + + if (ns.database === '') { + // neither database nor collection are present, top level instance view + ReactDOM.render( + React.createElement(this.serverStatsView, {interval: 1000}), + containerNode + ); + } else if (ns.collection === '') { + // a database was clicked, render collections table + ReactDOM.render( + React.createElement(this.collectionsTable), + containerNode + ); + } else { + // unmount instance/databases view and switch to collection view + ReactDOM.unmountComponentAtNode(containerNode); + } + this.updateTitle(namespace); + }, showTour: function(force) { var tourView = new TourView({force: force}); if (tourView.features.length > 0) { @@ -146,10 +165,10 @@ var HomeView = View.extend({ } } }, - updateTitle: function(model) { + updateTitle: function(ns) { var title = 'MongoDB Compass - ' + app.connection.instance_id; - if (model) { - title += '/' + model.getId(); + if (ns) { + title += '/' + ns; } document.title = title; }, @@ -170,26 +189,27 @@ var HomeView = View.extend({ return database.collections.get(ns.ns); }, - onNamespaceChange: function() { - const model = this._getCollection(); - - if (!model) { - app.navigate('/'); - return; - } - - const collection = app.instance.collections; - if (!collection.select(model)) { - return debug('already selected %s', model); - } - - this.updateTitle(model); - this.showNoCollectionsZeroState = false; - this.showDefaultZeroState = false; - app.navigate(format('schema/%s', model.getId()), { - silent: true - }); - }, + // onNamespaceChange: function(ns) { + // const model = this._getCollection(); + // + // // if (!model) { + // // app.navigate('/'); + // // return; + // // } + // + // const collection = app.instance.collections; + // if (!collection.select(model)) { + // return debug('already selected %s', model); + // } + // + // this.updateTitle(model); + // this.showNoCollectionsZeroState = false; + // this.showDefaultZeroState = false; + // + // // app.navigate(format('schema/%s', model.getId()), { + // // silent: true + // // }); + // }, onClickShowConnectWindow: function() { // code to close current connection window and open connect dialog ipc.call('app:show-connect-window'); diff --git a/src/internal-packages/database/index.js b/src/internal-packages/database/index.js new file mode 100644 index 00000000000..d981c929591 --- /dev/null +++ b/src/internal-packages/database/index.js @@ -0,0 +1,19 @@ +const app = require('ampersand-app'); +const CollectionsTable = require('./lib/components'); + +/** + * Activate all the components in the Schema package. + */ +function activate() { + app.appRegistry.registerComponent('Database.CollectionsTable', CollectionsTable); +} + +/** + * Deactivate all the components in the Schema package. + */ +function deactivate() { + app.appRegistry.deregisterComponent('Database.CollectionsTable'); +} + +module.exports.activate = activate; +module.exports.deactivate = deactivate; diff --git a/src/internal-packages/database/lib/components/index.jsx b/src/internal-packages/database/lib/components/index.jsx new file mode 100644 index 00000000000..5a909f5ed96 --- /dev/null +++ b/src/internal-packages/database/lib/components/index.jsx @@ -0,0 +1,13 @@ +const React = require('react'); + +class CollectionsTable extends React.Component { + render() { + return ( +

I am a collections table view.

+ ); + } +} + +CollectionsTable.displayName = 'CollectionsTable'; + +module.exports = CollectionsTable; diff --git a/src/internal-packages/database/package.json b/src/internal-packages/database/package.json new file mode 100644 index 00000000000..c0b72a45b0f --- /dev/null +++ b/src/internal-packages/database/package.json @@ -0,0 +1,9 @@ +{ + "name": "database", + "productName": "Compass Database View", + "description": "Responsible for the view at the database level.", + "version": "0.0.1", + "authors": "MongoDB Inc.", + "private": true, + "main": "./index.js" +} diff --git a/src/internal-packages/explain/lib/stores/index.js b/src/internal-packages/explain/lib/stores/index.js index b89bbcfe763..c2b804ef5b5 100644 --- a/src/internal-packages/explain/lib/stores/index.js +++ b/src/internal-packages/explain/lib/stores/index.js @@ -3,6 +3,7 @@ const ExplainActions = require('../actions'); const StateMixin = require('reflux-state-mixin'); const app = require('ampersand-app'); const NamespaceStore = require('hadron-reflux-store').NamespaceStore; +const toNS = require('mongodb-ns'); const ExplainPlanModel = require('mongodb-explain-plan-model'); const _ = require('lodash'); @@ -117,8 +118,11 @@ const CompassExplainStore = Reflux.createStore({ const QueryStore = app.appRegistry.getStore('Query.Store'); const filter = QueryStore.state.query; const options = {}; - - app.dataService.explain(NamespaceStore.ns, filter, options, (err, explain) => { + const ns = toNS(NamespaceStore.ns); + if (!ns.database || !ns.collection) { + return; + } + app.dataService.explain(ns.ns, filter, options, (err, explain) => { if (err) { return debug('error fetching explain plan:', err); } diff --git a/src/internal-packages/schema/lib/store/index.jsx b/src/internal-packages/schema/lib/store/index.jsx index bfac8321569..93255508172 100644 --- a/src/internal-packages/schema/lib/store/index.jsx +++ b/src/internal-packages/schema/lib/store/index.jsx @@ -2,6 +2,8 @@ const app = require('ampersand-app'); const Reflux = require('reflux'); const StateMixin = require('reflux-state-mixin'); const schemaStream = require('mongodb-schema').stream; +const toNS = require('mongodb-ns'); + const _ = require('lodash'); const ReadPreference = require('mongodb').ReadPreference; @@ -34,9 +36,11 @@ const SchemaStore = Reflux.createStore({ * Initialize the document list store. */ init: function() { - NamespaceStore.listen(() => { - this._reset(); - SchemaAction.startSampling(); + NamespaceStore.listen((ns) => { + if (ns && toNS(ns).collection) { + this._reset(); + SchemaAction.startSampling(); + } }); this.samplingStream = null; diff --git a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx index d5553c75ae4..5bdd42831e5 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx @@ -1,6 +1,7 @@ const React = require('react'); const SidebarCollection = require('./sidebar-collection'); -const debug = require('debug')('mongodb-compass:sidebar'); +const { NamespaceStore } = require('hadron-reflux-store'); +// const debug = require('debug')('mongodb-compass:sidebar'); class SidebarDatabase extends React.Component { constructor() { @@ -33,7 +34,7 @@ class SidebarDatabase extends React.Component { } handleDBClick(db) { - debug('db clicked', db); + NamespaceStore.ns = db; } handleArrowClick() { diff --git a/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx b/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx index beb1c814931..6546453023e 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx @@ -1,5 +1,6 @@ const React = require('react'); const app = require('ampersand-app'); +const { NamespaceStore } = require('hadron-reflux-store'); const InstanceActions = app.appRegistry.getAction('App.InstanceActions'); @@ -34,11 +35,7 @@ class SidebarInstanceProperties extends React.Component { } handleClickHostname() { - app.navigate('/', { - params: { - connectionId: app.connection.getId() - } - }); + NamespaceStore.ns = ''; } render() { diff --git a/src/internal-packages/validation/lib/stores/index.js b/src/internal-packages/validation/lib/stores/index.js index 27fad972b00..92cfafa3508 100644 --- a/src/internal-packages/validation/lib/stores/index.js +++ b/src/internal-packages/validation/lib/stores/index.js @@ -35,9 +35,10 @@ const ValidationStore = Reflux.createStore({ init() { this.lastFetchedValidatorDoc = {}; - NamespaceStore.listen(() => { - debug('new namespace'); - ValidationActions.fetchValidationRules(); + NamespaceStore.listen((ns) => { + if (ns && toNS(ns).collection) { + ValidationActions.fetchValidationRules(); + } }); }, From a2914dc44f9ba742990be2747eae9937f5b21ea6 Mon Sep 17 00:00:00 2001 From: Thomas Rueckstiess Date: Mon, 31 Oct 2016 22:48:51 +1100 Subject: [PATCH 2/7] COMPASS-80 add database level view, collections table --- src/app/index.less | 1 + src/internal-packages/app/index.js | 3 + .../lib/components/tab-nav-bar.jsx} | 12 +- src/internal-packages/app/styles/index.less | 1 + .../app/styles/sortable-table.less | 2 +- .../app/styles/tab-nav-bar.less | 109 ++++++++++++++++++ .../lib/actions/collections-actions.js | 12 ++ .../lib/components/collections-table.jsx | 57 +++++++++ .../lib/components/connected-collections.jsx | 28 +++++ .../database/lib/components/index.jsx | 30 ++++- .../database/lib/stores/collections-store.jsx | 104 +++++++++++++++++ .../database/styles/index.less | 8 ++ .../server-stats/lib/component/index.jsx | 10 +- .../server-stats/styles/rt-nav.less | 50 -------- .../lib/components/sidebar-collection.jsx | 4 +- .../lib/components/sidebar-database.jsx | 4 +- 16 files changed, 368 insertions(+), 67 deletions(-) rename src/internal-packages/{server-stats/lib/component/navbar-component.jsx => app/lib/components/tab-nav-bar.jsx} (72%) create mode 100644 src/internal-packages/app/styles/tab-nav-bar.less create mode 100644 src/internal-packages/database/lib/actions/collections-actions.js create mode 100644 src/internal-packages/database/lib/components/collections-table.jsx create mode 100644 src/internal-packages/database/lib/components/connected-collections.jsx create mode 100644 src/internal-packages/database/lib/stores/collections-store.jsx create mode 100644 src/internal-packages/database/styles/index.less diff --git a/src/app/index.less b/src/app/index.less index 576bc3058f8..85580c89c4d 100644 --- a/src/app/index.less +++ b/src/app/index.less @@ -26,3 +26,4 @@ @import "../internal-packages/explain/styles/index.less"; @import "../internal-packages/sidebar/styles/index.less"; @import "../internal-packages/validation/styles/index.less"; +@import "../internal-packages/database/styles/index.less"; diff --git a/src/internal-packages/app/index.js b/src/internal-packages/app/index.js index 327bea07f52..b587cc28a5d 100644 --- a/src/internal-packages/app/index.js +++ b/src/internal-packages/app/index.js @@ -1,6 +1,7 @@ const app = require('ampersand-app'); const StoreConnector = require('./lib/components/store-connector'); const SortableTable = require('./lib/components/sortable-table'); +const TabNavBar = require('./lib/components/tab-nav-bar'); const InstanceActions = require('./lib/actions/instance-actions'); const InstanceStore = require('./lib/stores/instance-store'); const ModalStatusMessage = require('./lib/components/modal-status-message'); @@ -12,6 +13,7 @@ function activate() { app.appRegistry.registerComponent('App.StoreConnector', StoreConnector); app.appRegistry.registerComponent('App.SortableTable', SortableTable); app.appRegistry.registerComponent('App.ModalStatusMessage', ModalStatusMessage); + app.appRegistry.registerComponent('App.TabNavBar', TabNavBar); app.appRegistry.registerAction('App.InstanceActions', InstanceActions); app.appRegistry.registerStore('App.InstanceStore', InstanceStore); } @@ -23,6 +25,7 @@ function deactivate() { app.appRegistry.deregisterComponent('App.StoreConnector'); app.appRegistry.deregisterComponent('App.SortableTable'); app.appRegistry.deregisterComponent('App.ModalStatusMessage'); + app.appRegistry.deregisterComponent('App.TabNavBar'); app.appRegistry.deregisterAction('App.InstanceActions'); app.appRegistry.deregisterStore('App.InstanceStore'); } diff --git a/src/internal-packages/server-stats/lib/component/navbar-component.jsx b/src/internal-packages/app/lib/components/tab-nav-bar.jsx similarity index 72% rename from src/internal-packages/server-stats/lib/component/navbar-component.jsx rename to src/internal-packages/app/lib/components/tab-nav-bar.jsx index 074fd0a521b..8ac18905471 100644 --- a/src/internal-packages/server-stats/lib/component/navbar-component.jsx +++ b/src/internal-packages/app/lib/components/tab-nav-bar.jsx @@ -30,11 +30,11 @@ class NavBarComponent extends React.Component { renderTabs() { const listItems = _.map(this.props.tabs, (tab, idx) => ( -
  • - {tab} +
  • + {tab}
  • )); - return ; + return ; } renderActiveView() { @@ -44,8 +44,8 @@ class NavBarComponent extends React.Component { render() { return ( -
    -
    +
    +
    {this.renderTabs()}
    {this.renderActiveView()} @@ -55,6 +55,7 @@ class NavBarComponent extends React.Component { } NavBarComponent.propTypes = { + theme: React.PropTypes.oneOf(['dark', 'light']), activeTabIndex: React.PropTypes.number, tabs: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, views: React.PropTypes.arrayOf(React.PropTypes.element).isRequired, @@ -62,6 +63,7 @@ NavBarComponent.propTypes = { }; NavBarComponent.defaultProps = { + theme: 'light', activeTabIndex: 0 }; diff --git a/src/internal-packages/app/styles/index.less b/src/internal-packages/app/styles/index.less index ebf806f4048..273b79c20c3 100644 --- a/src/internal-packages/app/styles/index.less +++ b/src/internal-packages/app/styles/index.less @@ -1,2 +1,3 @@ @import './sortable-table.less'; @import './modal-status-message.less'; +@import './tab-nav-bar.less'; diff --git a/src/internal-packages/app/styles/sortable-table.less b/src/internal-packages/app/styles/sortable-table.less index 7568b2bb0bf..2a421f50b43 100644 --- a/src/internal-packages/app/styles/sortable-table.less +++ b/src/internal-packages/app/styles/sortable-table.less @@ -14,7 +14,7 @@ user-select: none; -webkit-user-select: none; padding-left: 24px; - background-color: #f5f6f7; + background-color: @gray8; &-is-active { .sortable-table-sort-icon { diff --git a/src/internal-packages/app/styles/tab-nav-bar.less b/src/internal-packages/app/styles/tab-nav-bar.less new file mode 100644 index 00000000000..ddb9fae8973 --- /dev/null +++ b/src/internal-packages/app/styles/tab-nav-bar.less @@ -0,0 +1,109 @@ +.tab-nav-bar { + + &-header { + height: 45px; + background-color: @pw; + display: flex; + justify-content: flex-start; + align-items: flex-end; + position: relative; + } + + &-tabs { + display: flex; + justify-content: flex-end; + align-self: flex-end; + margin-bottom: 0; + position: absolute; + top: 16px; + margin-left: 10px; + } + + &-tab { + height: 30px; + width: 130px; + margin-right: 5px; + font-size: 13px; + text-transform: uppercase; + border-radius: 3px 3px 0 0; + display: flex; + justify-content: center; + cursor: pointer; + margin-bottom: 0; + color: #43B1E5; + } + + &-tab:hover { + background-color: @pw; + color: @gray0; + border: 1px solid @gray7; + + .tab-nav-bar-link { + color: @gray0; + } + } + + &-link { + margin: auto; + text-transform: uppercase; + font-size: 11px; + font-weight: bold; + color: #43B1E5; + } + + &-link:hover, &-link:active, &-link:focus { + text-decoration: none; + color: @gray0; + } + + &-is-selected { + .tab-nav-bar-link { + color: @gray0; + } + + &.tab-nav-bar-tab:hover { + background-color: @gray8; + border-bottom: none; + } + background-color: @gray8; + cursor: default; + border: #ebebed 1px solid; + border-bottom: none; + } + + &-is-dark-theme { + .tab-nav-bar-header { + background-color: #2B3033; + } + + .tab-nav-bar-tab { + color: #8B8D8F; + } + + .tab-nav-bar-link { + color: @pw; + } + + .tab-nav-bar-is-selected { + color: @pw; + background-color: #3D4247; + border-color: #545454; + + &.tab-nav-bar-tab:hover { + background-color: #3D4247; + } + } + + .tab-nav-bar-tab:hover { + background-color: #2B3033; + border-color: #545454; + border-bottom: none; + + color: @pw; + + .tab-nav-bar-link { + color: @pw; + } + } + } +} diff --git a/src/internal-packages/database/lib/actions/collections-actions.js b/src/internal-packages/database/lib/actions/collections-actions.js new file mode 100644 index 00000000000..8520ee37f8a --- /dev/null +++ b/src/internal-packages/database/lib/actions/collections-actions.js @@ -0,0 +1,12 @@ +const Reflux = require('reflux'); + +/** + * The actions used by the server stats components. + */ +const Actions = Reflux.createActions([ + 'sortCollections', + 'deleteCollection', + 'createCollection' +]); + +module.exports = Actions; diff --git a/src/internal-packages/database/lib/components/collections-table.jsx b/src/internal-packages/database/lib/components/collections-table.jsx new file mode 100644 index 00000000000..151241f9a2d --- /dev/null +++ b/src/internal-packages/database/lib/components/collections-table.jsx @@ -0,0 +1,57 @@ +const React = require('react'); +const app = require('ampersand-app'); +const CollectionsActions = require('../actions/collections-actions'); +const SortableTable = app.appRegistry.getComponent('App.SortableTable'); +const numeral = require('numeral'); + +const _ = require('lodash'); + +// const debug = require('debug')('mongodb-compass:server-stats:databases'); + +class DatabasesTable extends React.Component { + + onColumnHeaderClicked(column, order) { + CollectionsActions.sortCollections(column, order); + } + + onRowDeleteButtonClicked(collName) { + // CollectionsActions.deleteCollection(collName); + } + + render() { + // convert storage size to human-readable units (MB, GB, ...) + // we do this here so that sorting is not affected in the store + const rows = _.map(this.props.collections, (db) => { + return _.assign({}, db, { + 'Storage Size': numeral(db['Storage Size']).format('0.0b') + }); + }); + + return ( +
    + +
    + ); + } +} + +DatabasesTable.propTypes = { + columns: React.PropTypes.arrayOf(React.PropTypes.string), + collections: React.PropTypes.arrayOf(React.PropTypes.object), + sortOrder: React.PropTypes.oneOf(['asc', 'desc']), + sortColumn: React.PropTypes.string +}; + +DatabasesTable.displayName = 'DatabasesTable'; + +module.exports = DatabasesTable; diff --git a/src/internal-packages/database/lib/components/connected-collections.jsx b/src/internal-packages/database/lib/components/connected-collections.jsx new file mode 100644 index 00000000000..4251ec126cd --- /dev/null +++ b/src/internal-packages/database/lib/components/connected-collections.jsx @@ -0,0 +1,28 @@ +const React = require('react'); + +const app = require('ampersand-app'); +const StoreConnector = app.appRegistry.getComponent('App.StoreConnector'); +const CollectionsStore = require('../stores/collections-store'); +const CollectionsTable = require('./collections-table'); + +// const debug = require('debug')('mongodb-compass:compass-explain:index'); + +class ConnectedCollectionsTable extends React.Component { + + /** + * Connect CompassExplainComponent to store and render. + * + * @returns {React.Component} The rendered component. + */ + render() { + return ( + + + + ); + } +} + +ConnectedCollectionsTable.displayName = 'ConnectedCollectionsTable'; + +module.exports = ConnectedCollectionsTable; diff --git a/src/internal-packages/database/lib/components/index.jsx b/src/internal-packages/database/lib/components/index.jsx index 5a909f5ed96..c6578b5a855 100644 --- a/src/internal-packages/database/lib/components/index.jsx +++ b/src/internal-packages/database/lib/components/index.jsx @@ -1,13 +1,35 @@ const React = require('react'); +const app = require('ampersand-app'); +const CollectionsTableView = require('./connected-collections'); -class CollectionsTable extends React.Component { +class DatabaseView extends React.Component { + + constructor(props) { + super(props); + this.TabNavBar = app.appRegistry.getComponent('App.TabNavBar'); + } + + /** + * Renders the component. + * + * @returns {React.Component} The component. + */ render() { + const collectionsTableView = ; return ( -

    I am a collections table view.

    +
    + +
    ); } } -CollectionsTable.displayName = 'CollectionsTable'; +DatabaseView.displayName = 'DatabaseView'; -module.exports = CollectionsTable; +module.exports = DatabaseView; diff --git a/src/internal-packages/database/lib/stores/collections-store.jsx b/src/internal-packages/database/lib/stores/collections-store.jsx new file mode 100644 index 00000000000..eaa41cd060d --- /dev/null +++ b/src/internal-packages/database/lib/stores/collections-store.jsx @@ -0,0 +1,104 @@ +const Reflux = require('reflux'); +const StateMixin = require('reflux-state-mixin'); +const CollectionsActions = require('../actions/collections-actions'); +const NamespaceStore = require('hadron-reflux-store').NamespaceStore; +const app = require('ampersand-app'); +const toNS = require('mongodb-ns'); +const _ = require('lodash'); + +const debug = require('debug')('mongodb-compass:stores:collections'); + +const COLL_COLUMNS = ['Collection Name', 'Num. Documents', 'Avg. Document Size', 'Num. Indexes']; + +/** + * Databases store, used to present a table of databases with some basic + * stats, and allow DDL (create / delete databases). + * + * This store refreshes every time the App.InstanceStore changes. + */ +const CollectionsStore = Reflux.createStore({ + /** + * adds a state to the store, similar to React.Component's state + * @see https://github.com/yonatanmn/Super-Simple-Flux#reflux-state-mixin + */ + mixins: [StateMixin.store], + + /** + * listen to all database related actions + */ + listenables: CollectionsActions, + + /** + * Initialize everything that is not part of the store's state. + */ + init() { + this.InstanceStore = app.appRegistry.getStore('App.InstanceStore'); + NamespaceStore.listen(this.onNamespaceChanged.bind(this)); + // this.listenToExternalStore('App.InstanceStore', ); + this.indexes = []; + }, + + getInitialState() { + return { + columns: COLL_COLUMNS, + collections: [], + sortOrder: 'asc', + sortColumn: 'Collection Name' + }; + }, + + _sort(collections, column, order) { + return _.sortByOrder(collections, + column || this.state.sortColumn, order || this.state.sortOrder); + }, + + onNamespaceChanged() { + const ns = toNS(NamespaceStore.ns); + if (!ns.database) { + this.setState({ + collections: [] + }); + return; + } + + const instance = this.InstanceStore.state.instance; + const database = instance.databases.get(ns.database); + + const unsorted = database.collections.map((db) => { + return _.zipObject(COLL_COLUMNS, [ + toNS(db._id).collection + ]); + }); + + this.setState({ + collections: this._sort(unsorted) + }); + }, + + sortCollections(column, order) { + this.setState({ + collections: this._sort(this.state.collections, column, order), + sortColumn: column, + sortOrder: order + }); + }, + + // deleteCollection(dbName) { + // // TODO remove collection on server + // }, + // + // createCollection(collName) { + // // TODO create collection + // } + + /** + * log changes to the store as debug messages. + * @param {Object} prevState previous state. + */ + storeDidUpdate(prevState) { + debug('collections store changed from', prevState, 'to', this.state); + } + +}); + +module.exports = CollectionsStore; diff --git a/src/internal-packages/database/styles/index.less b/src/internal-packages/database/styles/index.less new file mode 100644 index 00000000000..7a7c151ada5 --- /dev/null +++ b/src/internal-packages/database/styles/index.less @@ -0,0 +1,8 @@ +.collections-table { + font-family: "Akzidenz", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 500; + background-color: @gray8; + height: 100vh; + min-width: 100%; + overflow-x: scroll; +} diff --git a/src/internal-packages/server-stats/lib/component/index.jsx b/src/internal-packages/server-stats/lib/component/index.jsx index 60816d7817e..409e91de17e 100644 --- a/src/internal-packages/server-stats/lib/component/index.jsx +++ b/src/internal-packages/server-stats/lib/component/index.jsx @@ -2,7 +2,7 @@ const React = require('react'); const Actions = require('../action'); const Performance = require('./performance-component'); const Databases = require('./connected-databases'); -const NavBarComponent = require('./navbar-component'); +const app = require('ampersand-app'); // const debug = require('debug')('mongodb-compass:server-stats-RTSSComponent'); /** @@ -17,6 +17,7 @@ class RTSSComponent extends React.Component { */ constructor(props) { super(props); + this.TabNavBar = app.appRegistry.getComponent('App.TabNavBar'); } componentDidMount() { @@ -27,19 +28,18 @@ class RTSSComponent extends React.Component { * Renders the component. * * @returns {React.Component} The component. -
    - -
    */ render() { const performanceView = ; const databasesView = ; return (
    -
    ); diff --git a/src/internal-packages/server-stats/styles/rt-nav.less b/src/internal-packages/server-stats/styles/rt-nav.less index d921242ab20..bc7d066c23b 100644 --- a/src/internal-packages/server-stats/styles/rt-nav.less +++ b/src/internal-packages/server-stats/styles/rt-nav.less @@ -1,11 +1,3 @@ -.rt-nav { - height: 45px; - background-color: #2B3033; - display: flex; - justify-content: flex-start; - align-items: flex-end; - position: relative; -} /* time indicator */ .time { border-radius: 3px 3px 3px 3px; @@ -68,45 +60,3 @@ margin-right: 7px; color: hsla(0, 0%, 100%, .7); } - -/* tabs */ -.rt-nav__tabs { - display: flex; - justify-content: flex-end; - align-self: flex-end; - margin-bottom: 0; - position: absolute; - top: 16px; - margin-left: 10px; -} - -.rt-nav__tab { - height: 30px; - width: 130px; - font-size: 13px; - text-transform: uppercase; - border-radius: 3px 3px 0 0; - display: flex; - justify-content: center; - cursor: pointer; - margin-bottom: 0; - color: #8B8D8F; -} -.rt-nav__link { - margin: auto; - text-transform: uppercase; - font-size: 11px; - font-weight: bold; - color: white; -} -.rt-nav__link:hover, .rt-nav__link:active, .rt-nav__link:focus { - text-decoration: none; - color: white; -} -.rt-nav--selected { - color: #FFFFFF; - background-color: #3D4247; - cursor: default; - border: #545454 1px solid; - border-bottom: none; -} diff --git a/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx b/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx index 5db20ba3d41..00aabe7b01a 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx @@ -16,7 +16,9 @@ class SidebarCollection extends React.Component { } handleClick() { - NamespaceStore.ns = this.props._id; + if (NamespaceStore.ns !== this.props._id) { + NamespaceStore.ns = this.props._id; + } } render() { diff --git a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx index 5bdd42831e5..2e05597101d 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx @@ -34,7 +34,9 @@ class SidebarDatabase extends React.Component { } handleDBClick(db) { - NamespaceStore.ns = db; + if (NamespaceStore.ns !== db) { + NamespaceStore.ns = db; + } } handleArrowClick() { From eada0ed071e10e5e31f568133b331969c5d11369 Mon Sep 17 00:00:00 2001 From: Thomas Rueckstiess Date: Tue, 1 Nov 2016 11:50:04 +1100 Subject: [PATCH 3/7] use dataService.database(), add columns, styling --- .../lib/components/collections-table.jsx | 20 +++++++-- .../database/lib/components/index.jsx | 5 ++- .../database/lib/stores/collections-store.jsx | 45 ++++++++++++++----- 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/src/internal-packages/database/lib/components/collections-table.jsx b/src/internal-packages/database/lib/components/collections-table.jsx index 151241f9a2d..33a13990e0c 100644 --- a/src/internal-packages/database/lib/components/collections-table.jsx +++ b/src/internal-packages/database/lib/components/collections-table.jsx @@ -19,11 +19,23 @@ class DatabasesTable extends React.Component { } render() { - // convert storage size to human-readable units (MB, GB, ...) + // convert some of the values to human-readable units (MB, GB, ...) // we do this here so that sorting is not affected in the store - const rows = _.map(this.props.collections, (db) => { - return _.assign({}, db, { - 'Storage Size': numeral(db['Storage Size']).format('0.0b') + // + // 'Collection Name', + // 'Num. Documents', + // 'Avg. Document Size', + // 'Total Document Size', + // 'Num. Indexes', + // 'Total Index Size' + + const rows = _.map(this.props.collections, (coll) => { + return _.assign({}, coll, { + 'Documents': numeral(coll.Documents).format('0,0'), + 'Avg. Document Size': _.isNaN(coll['Avg. Document Size']) ? + '-' : numeral(coll['Avg. Document Size']).format('0.0 b'), + 'Total Document Size': numeral(coll['Total Document Size']).format('0.0 b'), + 'Total Index Size': numeral(coll['Total Index Size']).format('0.0 b') }); }); diff --git a/src/internal-packages/database/lib/components/index.jsx b/src/internal-packages/database/lib/components/index.jsx index c6578b5a855..440eca7329d 100644 --- a/src/internal-packages/database/lib/components/index.jsx +++ b/src/internal-packages/database/lib/components/index.jsx @@ -18,13 +18,14 @@ class DatabaseView extends React.Component { const collectionsTableView = ; return (
    - + /> */}
    ); } diff --git a/src/internal-packages/database/lib/stores/collections-store.jsx b/src/internal-packages/database/lib/stores/collections-store.jsx index eaa41cd060d..e0ab4e05203 100644 --- a/src/internal-packages/database/lib/stores/collections-store.jsx +++ b/src/internal-packages/database/lib/stores/collections-store.jsx @@ -8,7 +8,14 @@ const _ = require('lodash'); const debug = require('debug')('mongodb-compass:stores:collections'); -const COLL_COLUMNS = ['Collection Name', 'Num. Documents', 'Avg. Document Size', 'Num. Indexes']; +const COLL_COLUMNS = [ + 'Collection Name', + 'Documents', + 'Avg. Document Size', + 'Total Document Size', + 'Num. Indexes', + 'Total Index Size' +]; /** * Databases store, used to present a table of databases with some basic @@ -43,7 +50,9 @@ const CollectionsStore = Reflux.createStore({ columns: COLL_COLUMNS, collections: [], sortOrder: 'asc', - sortColumn: 'Collection Name' + sortColumn: 'Collection Name', + fetchState: 'initial', + errorMessage: '' }; }, @@ -61,17 +70,29 @@ const CollectionsStore = Reflux.createStore({ return; } - const instance = this.InstanceStore.state.instance; - const database = instance.databases.get(ns.database); - - const unsorted = database.collections.map((db) => { - return _.zipObject(COLL_COLUMNS, [ - toNS(db._id).collection - ]); - }); + app.dataService.database(ns.database, {}, (err, res) => { + if (err) { + this.setState({ + fetchState: 'error', + errorMessage: err + }); + return; + } + debug('collections', res.collections); + const unsorted = _.map(res.collections, (coll) => { + return _.zipObject(COLL_COLUMNS, [ + coll.name, // Collection Name + coll.document_count, // Num. Documents + coll.size / coll.document_count, // Avg. Document Size + coll.size, // Total Document Size + coll.index_count, // Num Indexes + coll.index_size // Total Index Size + ]); + }); - this.setState({ - collections: this._sort(unsorted) + this.setState({ + collections: this._sort(unsorted) + }); }); }, From f5ac459140ff0e4b2fe02b8f9deaf53dfe38bea5 Mon Sep 17 00:00:00 2001 From: Thomas Rueckstiess Date: Tue, 1 Nov 2016 11:52:06 +1100 Subject: [PATCH 4/7] :shirt: fix linter errors. --- .../database/lib/components/collections-table.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal-packages/database/lib/components/collections-table.jsx b/src/internal-packages/database/lib/components/collections-table.jsx index 33a13990e0c..ec4e6c83b10 100644 --- a/src/internal-packages/database/lib/components/collections-table.jsx +++ b/src/internal-packages/database/lib/components/collections-table.jsx @@ -14,7 +14,7 @@ class DatabasesTable extends React.Component { CollectionsActions.sortCollections(column, order); } - onRowDeleteButtonClicked(collName) { + onRowDeleteButtonClicked(/* collName */) { // CollectionsActions.deleteCollection(collName); } From 095e3d4953fe90691bd000149abe59b4dfac6a80 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 1 Nov 2016 07:56:43 +0100 Subject: [PATCH 5/7] COMPASS-80: Fix app registry loading --- .../database/lib/components/collections-table.jsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/internal-packages/database/lib/components/collections-table.jsx b/src/internal-packages/database/lib/components/collections-table.jsx index ec4e6c83b10..a25c1b1bfd2 100644 --- a/src/internal-packages/database/lib/components/collections-table.jsx +++ b/src/internal-packages/database/lib/components/collections-table.jsx @@ -1,7 +1,6 @@ const React = require('react'); const app = require('ampersand-app'); const CollectionsActions = require('../actions/collections-actions'); -const SortableTable = app.appRegistry.getComponent('App.SortableTable'); const numeral = require('numeral'); const _ = require('lodash'); @@ -10,6 +9,11 @@ const _ = require('lodash'); class DatabasesTable extends React.Component { + constructor(props) { + super(props); + this.SortableTable = app.appRegistry.getComponent('App.SortableTable'); + } + onColumnHeaderClicked(column, order) { CollectionsActions.sortCollections(column, order); } @@ -41,7 +45,7 @@ class DatabasesTable extends React.Component { return (
    - Date: Tue, 1 Nov 2016 14:01:41 +0100 Subject: [PATCH 6/7] COMPASS-80: Fix zero state --- src/app/home/index.js | 1 + .../database/lib/components/collections-table.jsx | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/app/home/index.js b/src/app/home/index.js index 88e4afbb29e..b084fb35e2d 100644 --- a/src/app/home/index.js +++ b/src/app/home/index.js @@ -80,6 +80,7 @@ var HomeView = View.extend({ } else { this.tourClosed(); } + this.switchMainContent(''); }, switchMainContent: function(namespace) { if (namespace === this.ns) { diff --git a/src/internal-packages/database/lib/components/collections-table.jsx b/src/internal-packages/database/lib/components/collections-table.jsx index a25c1b1bfd2..6ac6979d8c3 100644 --- a/src/internal-packages/database/lib/components/collections-table.jsx +++ b/src/internal-packages/database/lib/components/collections-table.jsx @@ -7,7 +7,7 @@ const _ = require('lodash'); // const debug = require('debug')('mongodb-compass:server-stats:databases'); -class DatabasesTable extends React.Component { +class CollectionsTable extends React.Component { constructor(props) { super(props); @@ -61,13 +61,13 @@ class DatabasesTable extends React.Component { } } -DatabasesTable.propTypes = { +CollectionsTable.propTypes = { columns: React.PropTypes.arrayOf(React.PropTypes.string), collections: React.PropTypes.arrayOf(React.PropTypes.object), sortOrder: React.PropTypes.oneOf(['asc', 'desc']), sortColumn: React.PropTypes.string }; -DatabasesTable.displayName = 'DatabasesTable'; +CollectionsTable.displayName = 'CollectionsTable'; -module.exports = DatabasesTable; +module.exports = CollectionsTable; From 6bf079c08ae4a668d3c2b033a3820f4a3aa9e427 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 1 Nov 2016 14:08:38 +0100 Subject: [PATCH 7/7] COMPASS-80: Bring collection tab back --- src/internal-packages/database/lib/components/index.jsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/internal-packages/database/lib/components/index.jsx b/src/internal-packages/database/lib/components/index.jsx index 440eca7329d..7891293646e 100644 --- a/src/internal-packages/database/lib/components/index.jsx +++ b/src/internal-packages/database/lib/components/index.jsx @@ -18,14 +18,12 @@ class DatabaseView extends React.Component { const collectionsTableView = ; return (
    - {collectionsTableView} - {/* */} + className="rt-nav" />
    ); }