You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@superset.apache.org by GitBox <gi...@apache.org> on 2018/02/27 01:02:42 UTC

[GitHub] mistercrunch closed pull request #4463: New Landing Page v1.0

mistercrunch closed pull request #4463: New Landing Page v1.0
URL: https://github.com/apache/incubator-superset/pull/4463
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/superset/assets/javascripts/profile/components/RecentActivity.jsx b/superset/assets/javascripts/profile/components/RecentActivity.jsx
index 476f6d6180..7099a08649 100644
--- a/superset/assets/javascripts/profile/components/RecentActivity.jsx
+++ b/superset/assets/javascripts/profile/components/RecentActivity.jsx
@@ -10,10 +10,11 @@ const propTypes = {
 
 export default class RecentActivity extends React.PureComponent {
   render() {
+    const rowLimit = 50;
     const mutator = function (data) {
       return data.map(row => ({
-        action: row.action,
-        item: <a href={row.item_url}>{row.item_title}</a>,
+        name: <a href={row.item_url}>{row.item_title}</a>,
+        type: row.action,
         time: moment.utc(row.time).fromNow(),
         _time: row.time,
       }));
@@ -24,7 +25,7 @@ export default class RecentActivity extends React.PureComponent {
           className="table table-condensed"
           mutator={mutator}
           sortable
-          dataEndpoint={`/superset/recent_activity/${this.props.user.userId}/`}
+          dataEndpoint={`/superset/recent_activity/${this.props.user.userId}/?limit=${rowLimit}`}
         />
       </div>
     );
diff --git a/superset/assets/javascripts/profile/components/TableLoader.jsx b/superset/assets/javascripts/profile/components/TableLoader.jsx
index b14d6f6865..1e67426eea 100644
--- a/superset/assets/javascripts/profile/components/TableLoader.jsx
+++ b/superset/assets/javascripts/profile/components/TableLoader.jsx
@@ -1,9 +1,10 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import { Table, Tr, Td } from 'reactable';
-import { Collapse } from 'react-bootstrap';
 import $ from 'jquery';
 
+import '../../../stylesheets/reactable-pagination.css';
+
 const propTypes = {
   dataEndpoint: PropTypes.string.isRequired,
   mutator: PropTypes.func,
@@ -29,7 +30,7 @@ export default class TableLoader extends React.PureComponent {
   }
   render() {
     const tableProps = Object.assign({}, this.props);
-    let columns = this.props.columns;
+    let { columns } = this.props;
     if (!columns && this.state.data.length > 0) {
       columns = Object.keys(this.state.data[0]).filter(col => col[0] !== '_');
     }
@@ -40,25 +41,21 @@ export default class TableLoader extends React.PureComponent {
       return <img alt="loading" width="25" src="/static/assets/images/loading.gif" />;
     }
     return (
-      <Collapse in transitionAppear >
-        <div>
-          <Table {...tableProps}>
-            {this.state.data.map((row, i) => (
-              <Tr key={i}>
-                {columns.map((col) => {
-                  if (row.hasOwnProperty('_' + col)) {
-                    return (
-                      <Td key={col} column={col} value={row['_' + col]}>
-                        {row[col]}
-                      </Td>);
-                  }
-                  return <Td key={col} column={col}>{row[col]}</Td>;
-                })}
-              </Tr>
-            ))}
-          </Table>
-        </div>
-      </Collapse>
+      <Table {...tableProps} className="table" itemsPerPage={50} style={{ textTransform: 'capitalize' }}>
+        {this.state.data.map((row, i) => (
+          <Tr key={i}>
+            {columns.map((col) => {
+              if (row.hasOwnProperty('_' + col)) {
+                return (
+                  <Td key={col} column={col} value={row['_' + col]}>
+                    {row[col]}
+                  </Td>);
+              }
+              return <Td key={col} column={col}>{row[col]}</Td>;
+            })}
+          </Tr>
+        ))}
+      </Table>
     );
   }
 }
diff --git a/superset/assets/javascripts/welcome/App.jsx b/superset/assets/javascripts/welcome/App.jsx
index 78674c48f0..8f63719ea5 100644
--- a/superset/assets/javascripts/welcome/App.jsx
+++ b/superset/assets/javascripts/welcome/App.jsx
@@ -1,7 +1,14 @@
 import React from 'react';
-import { Panel, Row, Col, FormControl } from 'react-bootstrap';
-
+import PropTypes from 'prop-types';
+import { Panel, Row, Col, Tabs, Tab, FormControl } from 'react-bootstrap';
+import RecentActivity from '../profile/components/RecentActivity';
+import Favorites from '../profile/components/Favorites';
 import DashboardTable from './DashboardTable';
+import { t } from '../locales';
+
+const propTypes = {
+  user: PropTypes.object.isRequired,
+};
 
 export default class App extends React.PureComponent {
   constructor(props) {
@@ -17,24 +24,48 @@ export default class App extends React.PureComponent {
   render() {
     return (
       <div className="container welcome">
-        <Panel>
-          <Row>
-            <Col md={8}><h2>Dashboards</h2></Col>
-            <Col md={4}>
-              <FormControl
-                type="text"
-                bsSize="sm"
-                style={{ marginTop: '25px' }}
-                placeholder="Search"
-                value={this.state.search}
-                onChange={this.onSearchChange}
-              />
-            </Col>
-          </Row>
-          <hr />
-          <DashboardTable search={this.state.search} />
-        </Panel>
+        <Tabs defaultActiveKey={1} id="uncontrolled-tab-example">
+          <Tab eventKey={1} title={t('Recently Viewed')}>
+            <Panel>
+              <Row>
+                <Col md={8}><h2>{t('Recently Viewed')}</h2></Col>
+              </Row>
+              <hr />
+              <RecentActivity user={this.props.user} />
+            </Panel>
+          </Tab>
+          <Tab eventKey={2} title={t('Favorites')}>
+            <Panel>
+              <Row>
+                <Col md={8}><h2>{t('Favorites')}</h2></Col>
+              </Row>
+              <hr />
+              <Favorites user={this.props.user} />
+            </Panel>
+          </Tab>
+          <Tab eventKey={3} title={t('Dashboards')}>
+            <Panel>
+              <Row>
+                <Col md={8}><h2>{t('Dashboards')}</h2></Col>
+                <Col md={4}>
+                  <FormControl
+                    type="text"
+                    bsSize="sm"
+                    style={{ marginTop: '25px' }}
+                    placeholder="Search"
+                    value={this.state.search}
+                    onChange={this.onSearchChange}
+                  />
+                </Col>
+              </Row>
+              <hr />
+              <DashboardTable search={this.state.search} />
+            </Panel>
+          </Tab>
+        </Tabs>
       </div>
     );
   }
 }
+
+App.propTypes = propTypes;
diff --git a/superset/assets/javascripts/welcome/index.jsx b/superset/assets/javascripts/welcome/index.jsx
index 3994b9908b..df0f774a81 100644
--- a/superset/assets/javascripts/welcome/index.jsx
+++ b/superset/assets/javascripts/welcome/index.jsx
@@ -10,8 +10,13 @@ appSetup();
 
 const container = document.getElementById('app');
 const bootstrap = JSON.parse(container.getAttribute('data-bootstrap'));
+const user = {
+  ...bootstrap.user,
+};
 
 ReactDOM.render(
-  <App />,
+  <App
+    user={user}
+  />,
   container,
 );
diff --git a/superset/assets/spec/javascripts/welcome/App_spec.jsx b/superset/assets/spec/javascripts/welcome/App_spec.jsx
index 472c0e22e7..1fc2987a57 100644
--- a/superset/assets/spec/javascripts/welcome/App_spec.jsx
+++ b/superset/assets/spec/javascripts/welcome/App_spec.jsx
@@ -1,5 +1,5 @@
 import React from 'react';
-import { Panel, Col, Row } from 'react-bootstrap';
+import { Panel, Row, Tab } from 'react-bootstrap';
 import { shallow } from 'enzyme';
 import { describe, it } from 'mocha';
 import { expect } from 'chai';
@@ -13,10 +13,10 @@ describe('App', () => {
       React.isValidElement(<App {...mockedProps} />),
     ).to.equal(true);
   });
-  it('renders 2 Col', () => {
+  it('renders 4 Tab, Panel, and Row components', () => {
     const wrapper = shallow(<App {...mockedProps} />);
-    expect(wrapper.find(Panel)).to.have.length(1);
-    expect(wrapper.find(Row)).to.have.length(1);
-    expect(wrapper.find(Col)).to.have.length(2);
+    expect(wrapper.find(Tab)).to.have.length(3);
+    expect(wrapper.find(Panel)).to.have.length(3);
+    expect(wrapper.find(Row)).to.have.length(3);
   });
 });
diff --git a/superset/views/core.py b/superset/views/core.py
index c1c62796e0..b71f731732 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -4,7 +4,6 @@
 from __future__ import print_function
 from __future__ import unicode_literals
 
-from collections import defaultdict
 from datetime import datetime, timedelta
 import json
 import logging
@@ -21,7 +20,6 @@
 from flask_appbuilder.actions import action
 from flask_appbuilder.models.sqla.interface import SQLAInterface
 from flask_appbuilder.security.decorators import has_access_api
-from flask_appbuilder.security.sqla import models as ab_models
 from flask_babel import gettext as __
 from flask_babel import lazy_gettext as _
 import pandas as pd
@@ -51,6 +49,7 @@
     generate_download_headers, get_error_msg, get_user_roles,
     json_error_response, SupersetFilter, SupersetModelView, YamlExportMixin,
 )
+from .utils import bootstrap_user_data
 
 config = app.config
 stats_logger = config.get('STATS_LOGGER')
@@ -2555,9 +2554,12 @@ def welcome(self):
         """Personalized welcome page"""
         if not g.user or not g.user.get_id():
             return redirect(appbuilder.get_url_for_login)
+
         payload = {
+            'user': bootstrap_user_data(),
             'common': self.common_bootsrap_payload(),
         }
+
         return self.render_template(
             'superset/basic.html',
             entry='welcome',
@@ -2571,44 +2573,15 @@ def profile(self, username):
         """User profile page"""
         if not username and g.user:
             username = g.user.username
-        user = (
-            db.session.query(ab_models.User)
-            .filter_by(username=username)
-            .one()
-        )
-        roles = {}
-        permissions = defaultdict(set)
-        for role in user.roles:
-            perms = set()
-            for perm in role.permissions:
-                if perm.permission and perm.view_menu:
-                    perms.add(
-                        (perm.permission.name, perm.view_menu.name),
-                    )
-                    if perm.permission.name in ('datasource_access', 'database_access'):
-                        permissions[perm.permission.name].add(perm.view_menu.name)
-            roles[role.name] = [
-                [perm.permission.name, perm.view_menu.name]
-                for perm in role.permissions
-                if perm.permission and perm.view_menu
-            ]
+
         payload = {
-            'user': {
-                'username': user.username,
-                'firstName': user.first_name,
-                'lastName': user.last_name,
-                'userId': user.id,
-                'isActive': user.is_active(),
-                'createdOn': user.created_on.isoformat(),
-                'email': user.email,
-                'roles': roles,
-                'permissions': permissions,
-            },
+            'user': bootstrap_user_data(username, include_perms=True),
             'common': self.common_bootsrap_payload(),
         }
+
         return self.render_template(
             'superset/basic.html',
-            title=user.username + "'s profile",
+            title=username + "'s profile",
             entry='profile',
             bootstrap_data=json.dumps(payload, default=utils.json_iso_dttm_ser),
         )
diff --git a/superset/views/utils.py b/superset/views/utils.py
new file mode 100644
index 0000000000..b1f3fa2db7
--- /dev/null
+++ b/superset/views/utils.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+from __future__ import unicode_literals
+
+from collections import defaultdict
+
+from flask import g
+from flask_appbuilder.security.sqla import models as ab_models
+
+from superset import db
+
+
+def bootstrap_user_data(username=None, include_perms=False):
+    if username:
+        username = username
+    else:
+        username = g.user.username
+
+    user = (
+        db.session.query(ab_models.User)
+        .filter_by(username=username)
+        .one()
+    )
+
+    payload = {
+        'username': user.username,
+        'firstName': user.first_name,
+        'lastName': user.last_name,
+        'userId': user.id,
+        'isActive': user.is_active(),
+        'createdOn': user.created_on.isoformat(),
+        'email': user.email,
+    }
+
+    if include_perms:
+        roles, permissions = get_permissions(user)
+        payload['roles'] = roles
+        payload['permissions'] = permissions
+
+    return payload
+
+
+def get_permissions(user):
+    if not user.roles:
+        raise AttributeError('User object does not have roles')
+
+    roles = {}
+    permissions = defaultdict(set)
+    for role in user.roles:
+        perms = set()
+        for perm in role.permissions:
+            if perm.permission and perm.view_menu:
+                perms.add(
+                    (perm.permission.name, perm.view_menu.name),
+                )
+                if perm.permission.name in ('datasource_access',
+                                            'database_access'):
+                    permissions[perm.permission.name].add(perm.view_menu.name)
+        roles[role.name] = [
+            [perm.permission.name, perm.view_menu.name]
+            for perm in role.permissions
+            if perm.permission and perm.view_menu
+        ]
+
+    return roles, permissions


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services