You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by yj...@apache.org on 2020/10/29 05:44:43 UTC

[incubator-superset] branch home-screen-mvp created (now 6d3e012)

This is an automated email from the ASF dual-hosted git repository.

yjc pushed a change to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git.


      at 6d3e012  lint fix

This branch includes the following new commits:

     new f476c02  step 1: broken stuff!
     new 624f23e  first steps
     new 5e2b9a4  more adding and slicing
     new 64d806c  step 1: broken stuff!
     new b38ddfb  can now filter dashboards/charts for "Edited" tabs (filter by changed_by o_m)
     new fbd1511  more updates
     new 7935331  update recent card
     new b029d5e  add icon
     new 40d2acf  Adding Expand Icon to Collapse component
     new 97c21c9  more updates
     new 0847038  clean up code
     new bf28bb6  remove lock file
     new faf395e  remove consoles
     new 6971717  fixing subnav button height shift
     new 45b17fb  lil' ascii arrows
     new 1b46423  update branch
     new aa704b0  update test part 1
     new 4ae640e  remove consoles
     new 20915ae  fix typescript
     new a363a0f  add images and update emptystate
     new 9655b48  add changes
     new b7e35f9  update chart card
     new 92541f9  fix css issues from rebase
     new 659a49b  add suggestions
     new 8da172c  more changes
     new 065ad3c  update tests and clear typescript errors
     new 65b24f1  Update superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
     new f94502b  update from comments
     new 23f5424  more updates..
     new 629ad37  fix rebase
     new 46b9cf4  fix pesky type errors
     new 03e6fde  test fixes
     new 6d3e012  lint fix

The 33 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[incubator-superset] 12/33: remove lock file

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit bf28bb6ec56f9e13afe8b26cb625afd9d60575a2
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Thu Oct 8 14:27:51 2020 -0700

    remove lock file
---
 .../views/CRUD/welcome/ChartTable_spec.tsx         |  79 +++++
 .../views/CRUD/welcome/DashboardTable_spec.tsx     |   6 +-
 .../views/CRUD/welcome/SavedQueries_spec.tsx       |  32 ++
 .../views/CRUD/welcome/Welcome_spec.tsx            |   1 +
 .../src/components/ListViewCard/index.tsx          |   4 +-
 .../src/views/CRUD/chart/ChartCard.tsx             |   1 -
 .../src/views/CRUD/dashboard/DashboardList.tsx     |   4 +-
 superset-frontend/src/views/CRUD/utils.tsx         |  26 +-
 .../src/views/CRUD/welcome/ActivityTable.tsx       |  72 ++--
 .../src/views/CRUD/welcome/SavedQueries.tsx        |  19 +-
 .../src/views/CRUD/welcome/Welcome.tsx             | 365 ++++++++++-----------
 11 files changed, 360 insertions(+), 249 deletions(-)

diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx
new file mode 100644
index 0000000..ed0666e
--- /dev/null
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx
@@ -0,0 +1,79 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import { mount } from 'enzyme';
+import thunk from 'redux-thunk';
+import fetchMock from 'fetch-mock';
+
+import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
+import configureStore from 'redux-mock-store';
+import ChartTable from 'src/views/CRUD/welcome/ChartTable';
+import ChartCard from 'src/views/CRUD/chart/ChartCard';
+
+// store needed for withToasts(DashboardTable)
+const mockStore = configureStore([thunk]);
+const store = mockStore({});
+
+const chartsEndpoint = 'glob:*/api/v1/chart/?*';
+// fetchMock.get(chartsEndpoint, { result: mockDashboards });
+
+const mockCharts = [...new Array(3)].map((_, i) => ({
+  changed_on_utc: new Date().toISOString(),
+  created_by: 'super user',
+  id: i,
+  slice_name: `cool chart ${i}`,
+  url: 'url',
+  viz_type: 'bar',
+  datasource_title: `ds${i}`,
+  thumbnail_url: '/thumbnail',
+}));
+
+/* fetchMock.get(chartsEndpoint, {
+  result: [],
+});
+*/
+fetchMock.get(chartsEndpoint, {
+  result: mockCharts,
+});
+
+describe('DashboardTable', () => {
+  beforeEach(fetchMock.resetHistory);
+
+  const mockedProps = {};
+  const wrapper = mount(<ChartTable {...mockedProps} />, {
+    context: { store },
+  });
+
+  /*beforeAll(async () => {
+    await waitForComponentToPaint(wrapper);
+  });*/
+
+  it('it renders', () => {
+    expect(wrapper.find(ChartTable)).toExist();
+  });
+  it('fetches chart favorites and renders chart cards ', () => {
+      console.log('mock call', fetchMock.calls(/chart\/\?q/))
+    expect(fetchMock.calls(chartsEndpoint)).toHaveLength(1);
+    console.log('wrapper', wrapper.dive());
+    // there's a delay between response and updating state, so manually set it
+    // rather than adding a timeout which could introduce flakiness
+    // wrapper.setState({ dashboards: mockDashboards });
+    expect(wrapper.find(ChartCard)).toExist();
+  });
+});
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx
index e09b5fe..4d2eb9e 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx
@@ -23,8 +23,8 @@ import configureStore from 'redux-mock-store';
 import fetchMock from 'fetch-mock';
 import { supersetTheme, ThemeProvider } from '@superset-ui/core';
 
-import ListView from 'src/components/ListView';
 import DashboardTable from 'src/views/CRUD/welcome/DashboardTable';
+import DashboardCard from 'src/views/CRUD/welcome/DashboardCard';
 
 // store needed for withToasts(DashboardTable)
 const mockStore = configureStore([thunk]);
@@ -47,7 +47,7 @@ function setup() {
 describe('DashboardTable', () => {
   beforeEach(fetchMock.resetHistory);
 
-  it('fetches dashboards and renders a ListView', () => {
+  it('fetches dashboards and renders a ', () => {
     return new Promise(done => {
       const wrapper = setup();
 
@@ -56,7 +56,7 @@ describe('DashboardTable', () => {
         // there's a delay between response and updating state, so manually set it
         // rather than adding a timeout which could introduce flakiness
         wrapper.setState({ dashboards: mockDashboards });
-        expect(wrapper.find(ListView)).toExist();
+        expect(wrapper.find(DashboardCard)).toExist();
         done();
       });
     });
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
index e69de29..d5c61b5 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import { SavedQueries } from 'src/views/CRUD/welcome/SavedQueries'
+import { shallow } from 'enzyme';
+
+describe('SavedQueries', () => {
+  const 
+  it('is valid', () => {
+    expect(React.isValidElement(<SavedQueries />)).toBe(true);
+  });
+  it('SaveQueries renders up to three saved queries', ()=>{
+    //expect() 
+  })
+  
+});
\ No newline at end of file
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
index 5db15b9..13c27b7 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
@@ -40,4 +40,5 @@ describe('Welcome', () => {
     const wrapper = shallow(<Welcome {...mockedProps} />);
     expect(wrapper.find('SubMenu')).toHaveLength(4);
   });
+  console.log('wrapper', )
 });
diff --git a/superset-frontend/src/components/ListViewCard/index.tsx b/superset-frontend/src/components/ListViewCard/index.tsx
index 7fb69c0..427beba 100644
--- a/superset-frontend/src/components/ListViewCard/index.tsx
+++ b/superset-frontend/src/components/ListViewCard/index.tsx
@@ -156,6 +156,7 @@ interface CardProps {
   rows?: number | string;
   avatar?: string;
   isRecent?: boolean;
+  tableName?: string;
 }
 
 function ListViewCard({
@@ -169,6 +170,7 @@ function ListViewCard({
   coverRight,
   actions,
   avatar,
+  tableName,
   loading,
   imgPosition = 'top',
   showImg = true,
@@ -213,7 +215,7 @@ function ListViewCard({
             </div>
             <div>
               <div>Datasource Name</div>
-              <div>{}</div>
+              <div>{tableName}</div>
             </div>
           </QueryData>
         ))
diff --git a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
index 1046f3a..e1ab61f 100644
--- a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
+++ b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
@@ -112,7 +112,6 @@ export default function ChartCard({
       )}
     </Menu>
   );
-  console.log('favorite status', favoriteStatusRef)
   return (
     <ListViewCard
       loading={loading}
diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx
index 1fe8427..770122e 100644
--- a/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx
+++ b/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx
@@ -32,6 +32,8 @@ import Icon from 'src/components/Icon';
 import FaveStar from 'src/components/FaveStar';
 import PropertiesModal from 'src/dashboard/components/PropertiesModal';
 import TooltipWrapper from 'src/components/TooltipWrapper';
+
+import Dashboard from 'src/dashboard/containers/Dashboard';
 import DashboardCard from './DashboardCard';
 
 const PAGE_SIZE = 25;
@@ -415,7 +417,7 @@ function DashboardList(props: DashboardListProps) {
     },
   ];
 
-  function renderCard(dashboard: Dashboard & { loading: boolean }) {
+  function renderCard(dashboard: Dashboard) {
     return (
       <DashboardCard
         {...{
diff --git a/superset-frontend/src/views/CRUD/utils.tsx b/superset-frontend/src/views/CRUD/utils.tsx
index 0d5550e..bd6e9f0 100644
--- a/superset-frontend/src/views/CRUD/utils.tsx
+++ b/superset-frontend/src/views/CRUD/utils.tsx
@@ -53,18 +53,34 @@ const createFetchResourceMethod = (method: string) => (
   return [];
 };
 
-export const createBatchMethod = (queryParams: string) => {
-  return Promise.all([
+export const createBatchMethod = (queryParams: string, created?: string) => {
+  const baseBatch = [
     SupersetClient.get({ endpoint: `/api/v1/dashboard/?q=${queryParams}` }),
     SupersetClient.get({ endpoint: `/api/v1/chart/?q=${queryParams}` }),
-  ]).then(([dashboardRes, chartRes]) => {
+  ];
+  if (created)
+    baseBatch.push(
+      SupersetClient.get({ endpoint: `/api/v1/saved_query/?q=${queryParams}` }),
+    );
+  return Promise.all(baseBatch).then(([dashboardRes, chartRes, savedQuery]) => {
+    console.log('dashboard', dashboardRes);
+    console.log('chartRes', chartRes);
+    console.log('savedQuery', savedQuery);
     const results = [];
+    const ifQuery = savedQuery ? savedQuery.json?.result.slice(0, 3) : [];
+    console.log('-----slices--------');
+    console.log(dashboardRes?.json?.result.slice(0, 3));
+    console.log(chartRes?.json?.result.slice(0, 3));
+    console.log(ifQuery);
+    console.log('-----END Slices--------');
     results.push(
       ...[
-        ...dashboardRes.json.result.slice(0, 3),
-        ...chartRes.json.result.slice(0, 3),
+        ...dashboardRes.json?.result.slice(0, 3),
+        ...chartRes.json?.result.slice(0, 3),
+        ...ifQuery,
       ],
     );
+    console.log('result', results);
     return results;
   });
 };
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index de237b4..669838d 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -17,7 +17,6 @@
  * under the License.
  */
 import React, { useEffect, useState } from 'react';
-import { SupersetClient, t } from '@superset-ui/core';
 import rison from 'rison';
 import moment from 'moment';
 import ListViewCard from 'src/components/ListViewCard';
@@ -30,7 +29,12 @@ interface MapProps {
   slice_name: string;
   time: string;
   changed_on_utc: string;
-  item_url: string;
+  url: string;
+  sql: string;
+  dashboard_title: string;
+  label: string;
+  id: string;
+  table: object;
 }
 
 interface ActivityProps {
@@ -43,7 +47,8 @@ interface ActivityProps {
 export default function ActivityTable({ user, activityFilter }: ActivityProps) {
   const [active, setActiveState] = useState([]);
   const [loading, setLoading] = useState(false);
-  const recent = `/superset/recent_activity/${user.userId}/?limit=5`;
+  // this API uses Log for data which in some cases is can be empty
+  // const recent = `/superset/recent_activity/${user.userId}/?limit=5`;
   const filters = {
     // Chart and dashbaord uses same filters
     // for edited and created
@@ -63,31 +68,30 @@ export default function ActivityTable({ user, activityFilter }: ActivityProps) {
     ],
   };
 
-  const setData = (endpoint: string) => {
-    setLoading(true);
-    SupersetClient.get({ endpoint })
-      .then(({ json }) => {
-        setLoading(false);
+  const setBatchData = (q: string, created?: string) => {
+    createBatchMethod(q, created)
+      .then((res: Array<object>) =>
         // @ts-ignore
-        setActiveState(json);
-      })
-      .catch(() => {
-        setLoading(false);
-        createErrorHandler(() =>
-          addDangerToast(t('There was an issue fetching your resource')),
-        );
-      });
+        setActiveState(res),
+      )
+      .catch(() => addDangerToast('Oops something went wrong'));
   };
 
-  const setBatchData = (q: string) => {
-    // @ts-ignore
-    createBatchMethod(q).then((res: Array<object>) => setActiveState(res));
+  const getFilterTitle = (e: MapProps) => {
+    if (e.dashboard_title) return e.dashboard_title;
+    if (e.label) return e.label;
+    if (e.url && !e.table) return e.item_title;
+    return e.slice_name;
   };
 
-  const getIconName = (name: string | undefined) => {
-    if (name === 'explore_json') return 'sql';
-    if (name === 'dashboard') return 'nav-dashboard';
-    if (name === 'log' || name === 'explore') return 'nav-charts';
+  const getIconName = (e: MapProps) => {
+    if (e.sql) return 'sql';
+    if (e.url.indexOf('dashboard') !== -1) {
+      return 'nav-dashboard';
+    }
+    if (e.url.indexOf('explore') !== -1) {
+      return 'nav-charts';
+    }
     return '';
   };
 
@@ -99,14 +103,11 @@ export default function ActivityTable({ user, activityFilter }: ActivityProps) {
       page_size: 0,
       filters: activityFilter !== 'Created' ? filters.edited : filters.created,
     });
-    if (activityFilter === 'Viewed') {
-      setData(recent);
-    }
     if (activityFilter === 'Edited') {
       setBatchData(queryParams);
     }
     if (activityFilter === 'Created') {
-      setBatchData(queryParams);
+      setBatchData(queryParams, 'createdBy');
     }
   };
 
@@ -115,18 +116,17 @@ export default function ActivityTable({ user, activityFilter }: ActivityProps) {
   }, [activityFilter]);
 
   const renderActivity = () => {
-    return active.map((e: MapProps) => (
+    return active.map((e: MapProps, i) => (
       <ListViewCard
+        key={`${i}`}
         isRecent
         loading={loading}
-        imgURL={null}
-        imgFallbackURL={null}
-        url={e.item_url}
-        title={activityFilter === 'Viewed' ? e.item_title : e.slice_name}
-        description={moment
-          .utc(activityFilter === 'Viewd' ? e.time : e.changed_on_utc)
-          .fromNow()}
-        avatar={getIconName(e.action)}
+        imgURL=""
+        imgFallbackURL=""
+        url={e.sql ? `/supserset/sqllab?queryId=${e.id}` : e.url}
+        title={getFilterTitle(e)}
+        description={moment.utc(e.changed_on_utc).fromNow()}
+        avatar={getIconName(e)}
         actions={null}
       />
     ));
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
index d30db0b..c60d4fb 100644
--- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import React, { useEffect, useState } from 'react';
-import { t, SupersetClient } from '@superset-ui/core';
+import { t } from '@superset-ui/core';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import { Dropdown, Menu } from 'src/common/components';
 import { useListViewResource } from 'src/views/CRUD/hooks';
@@ -28,6 +28,7 @@ import { addDangerToast } from 'src/messageToasts/actions';
 const PAGE_SIZE = 3;
 
 interface Query {
+  sql_tables: array;
   database: {
     database_name: string;
   };
@@ -37,15 +38,18 @@ interface Query {
   addDangerToast: () => void;
 }
 
-interface StateProps {
-  queries: Array<Query>;
+interface SavedQueriesProps {
+  user: {
+    userId: string | number;
+  };
+  queryFilter: string;
 }
 
-const SavedQueries = ({ user, queryFilter }) => {
+const SavedQueries = ({ user, queryFilter }: SavedQueriesProps) => {
   const {
     state: { loading, resourceCollection: queries },
     fetchData,
-  } = useListViewResource<Query>('query', t('query'), addDangerToast);
+  } = useListViewResource<Query>('saved_query', t('query'), addDangerToast);
   const getFilters = () => {
     const filters = [];
 
@@ -90,10 +94,11 @@ const SavedQueries = ({ user, queryFilter }) => {
       {queries ? (
         queries.map(q => (
           <ListViewCard
-            imgFallbackURL={null}
-            imgURL={null}
+            imgFallbackURL="/static/assets/images/dashboard-card-fallback.png"
+            imgURL=""
             title={q.database.database_name}
             rows={q.rows}
+            tableName={q.sql_tables[0].table}
             loading={loading}
             description={t('Last run ', q.end_time)}
             showImg={false}
diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index b4326fc..f6015b7 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -17,11 +17,9 @@
  * under the License.
  */
 import React, { useState } from 'react';
-import { FormControl } from 'react-bootstrap';
 import SubMenu from 'src/components/Menu/SubMenu';
 import { styled, t } from '@superset-ui/core';
 import { Collapse } from 'src/common/components';
-import { useQueryParam, StringParam, QueryParamConfig } from 'use-query-params';
 import { User } from 'src/types/bootstrapTypes';
 import Icon from 'src/components/Icon';
 
@@ -36,9 +34,24 @@ interface WelcomeProps {
   user: User;
 }
 
+const WelcomeContainer = styled.div`
+  nav {
+    background-color: ${({ theme }) => theme.colors.grayscale.light4};
+    &:after {
+      content: '';
+      display: block;
+      border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
+      margin: 0px 26px;
+    }
+  }
+  .ant-card.ant-card-bordered {
+    border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
+  }
+`;
+
 const ActivityContainer = styled.div`
   display: grid;
-  grid-template-columns: repeat(auto-fit, minmax(300px, max-content));
+  grid-template-columns: repeat(auto-fit, minmax(31%, max-content));
   grid-gap: ${({ theme }) => theme.gridUnit * 8}px;
   justify-content: center;
   padding: ${({ theme }) => theme.gridUnit * 2}px
@@ -48,6 +61,7 @@ const ActivityContainer = styled.div`
 const IconContainer = styled.div`
   svg {
     vertical-align: -7px;
+    color: ${({ theme }) => theme.colors.primary.dark1};
   }
 `;
 export const CardContainer = styled.div`
@@ -56,219 +70,180 @@ export const CardContainer = styled.div`
   grid-gap: ${({ theme }) => theme.gridUnit * 8}px;
   justify-content: left;
   padding: ${({ theme }) => theme.gridUnit * 2}px
-    ${({ theme }) => theme.gridUnit * 4}px;
+    ${({ theme }) => theme.gridUnit * 6}px;
 `;
 
-function useSyncQueryState(
-  queryParam: string,
-  queryParamType: QueryParamConfig<
-    string | null | undefined,
-    string | undefined
-  >,
-  defaultState: string,
-): [string, (val: string) => void] {
-  const [queryState, setQueryState] = useQueryParam(queryParam, queryParamType);
-  const [state, setState] = useState(queryState || defaultState);
-
-  const setQueryStateAndState = (val: string) => {
-    setQueryState(val);
-    setState(val);
-  };
-
-  return [state, setQueryStateAndState];
-}
-
 export default function Welcome({ user }: WelcomeProps) {
   const [queryFilter, setQueryFilter] = useState('Favorite');
-  const [activityFilter, setActivityFilter] = useState('Viewed');
+  const [activityFilter, setActivityFilter] = useState('Edited');
   const [dashboardFilter, setDashboardFilter] = useState('Favorite');
   const [chartFilter, setChartFilter] = useState('Favorite');
-  const [searchQuery, setSearchQuery] = useSyncQueryState(
-    'search',
-    StringParam,
-    '',
-  );
 
   function ExpandIcon(): React.ReactNode {
     return <Icon name="caret-right" />;
   }
 
   return (
-    <Collapse defaultActiveKey={['1']} expandIcon={ExpandIcon} ghost>
-      <Panel header={t('Recents')} key="1">
-        <SubMenu
-          activeChild={activityFilter}
-          name=""
-          // eslint-disable-next-line react/no-children-prop
-          children={[
-            {
-              name: 'Viewed',
-              label: t('Viewed'),
-              onClick: () => setActivityFilter('Viewed'),
-            },
-            {
-              name: 'Edited',
-              label: t('Edited'),
-              onClick: () => setActivityFilter('Edited'),
-            },
-            {
-              name: 'Created',
-              label: t('Created'),
-              onClick: () => setActivityFilter('Created'),
-            },
-          ]}
-        />
-        <ActivityContainer>
-          <ActivityTable user={user} activityFilter={activityFilter} />
-        </ActivityContainer>
-      </Panel>
+    <WelcomeContainer>
+      <Collapse defaultActiveKey={['1']} ghost>
+        <Panel header={t('Recents')} key="1">
+          <SubMenu
+            activeChild={activityFilter}
+            name=""
+            // eslint-disable-next-line react/no-children-prop
+            children={[
+              {
+                name: 'Edited',
+                label: t('Edited'),
+                onClick: () => setActivityFilter('Edited'),
+              },
+              {
+                name: 'Created',
+                label: t('Created'),
+                onClick: () => setActivityFilter('Created'),
+              },
+            ]}
+          />
+          <ActivityContainer>
+            <ActivityTable user={user} activityFilter={activityFilter} />
+          </ActivityContainer>
+        </Panel>
 
-      <Panel header={t('Dashboards')} key="2">
-        <SubMenu
-          activeChild={dashboardFilter}
-          name=""
-          // eslint-disable-next-line react/no-children-prop
-          children={[
-            {
-              name: 'Favorite',
-              label: t('Favorite'),
-              onClick: () => setDashboardFilter('Favorite'),
-            },
-            {
-              name: 'Mine',
-              label: t('Mine'),
-              onClick: () => setDashboardFilter('Mine'),
-            },
-          ]}
-          buttons={[
-            {
-              name: (
-                <IconContainer>
-                  <Icon name="plus-small" /> DashBoard{' '}
-                </IconContainer>
-              ),
-              buttonStyle: 'tertiary',
-              onClick: () => {
-                // @ts-ignore
-                window.location = '/dashboard/new';
+        <Panel header={t('Dashboards')} key="2">
+          <SubMenu
+            activeChild={dashboardFilter}
+            name=""
+            // eslint-disable-next-line react/no-children-prop
+            children={[
+              {
+                name: 'Favorite',
+                label: t('Favorite'),
+                onClick: () => setDashboardFilter('Favorite'),
+              },
+              {
+                name: 'Mine',
+                label: t('Mine'),
+                onClick: () => setDashboardFilter('Mine'),
               },
-            },
-            {
-              name: 'View All',
-              buttonStyle: 'link',
-              onClick: () => {
-                // @ts-ignore
-                window.location = '/dashboard/list/';
+            ]}
+            buttons={[
+              {
+                name: (
+                  <IconContainer>
+                    <Icon name="plus-small" /> Dashboard{' '}
+                  </IconContainer>
+                ),
+                buttonStyle: 'tertiary',
+                onClick: () => {
+                  // @ts-ignore
+                  window.location = '/dashboard/new';
+                },
               },
-            },
-          ]}
-        />
-        <FormControl
-          type="text"
-          bsSize="sm"
-          placeholder="Search"
-          value={searchQuery}
-          // @ts-ignore React bootstrap types aren't quite right here
-          onChange={e => setSearchQuery(e.currentTarget.value)}
-        />
-        <CardContainer>
-          <DashboardTable
-            search={searchQuery}
-            dashboardFilter={dashboardFilter}
-            user={user}
+              {
+                name: 'View All',
+                buttonStyle: 'link',
+                onClick: () => {
+                  // @ts-ignore
+                  window.location = '/dashboard/list/';
+                },
+              },
+            ]}
           />
-        </CardContainer>
-      </Panel>
+          <CardContainer>
+            <DashboardTable dashboardFilter={dashboardFilter} user={user} />
+          </CardContainer>
+        </Panel>
 
-      <Panel header={t('Saved Queries')} key="3">
-        <SubMenu
-          activeChild={queryFilter}
-          name=""
-          // eslint-disable-next-line react/no-children-prop
-          children={[
-            {
-              name: 'Favorite',
-              label: t('Favorite'),
-              onClick: () => setQueryFilter('Favorite'),
-            },
-            {
-              name: 'Mine',
-              label: t('Mine'),
-              onClick: () => setQueryFilter('Mine'),
-            },
-          ]}
-          buttons={[
-            {
-              name: (
-                <IconContainer>
-                  <Icon name="plus-small" /> SQL Query{' '}
-                </IconContainer>
-              ),
-              buttonStyle: 'tertiary',
-              onClick: () => {
-                // @ts-ignore
-                window.location = '/superset/sqllab';
+        <Panel header={t('Saved Queries')} key="3">
+          <SubMenu
+            activeChild={queryFilter}
+            name=""
+            // eslint-disable-next-line react/no-children-prop
+            children={[
+              {
+                name: 'Favorite',
+                label: t('Favorite'),
+                onClick: () => setQueryFilter('Favorite'),
+              },
+              {
+                name: 'Mine',
+                label: t('Mine'),
+                onClick: () => setQueryFilter('Mine'),
               },
-            },
-            {
-              name: 'View All',
-              buttonStyle: 'link',
-              onClick: () => {
-                // @ts-ignore
-                window.location = 'superset/sqllab#search';
+            ]}
+            buttons={[
+              {
+                name: (
+                  <IconContainer>
+                    <Icon name="plus-small" /> SQL Query{' '}
+                  </IconContainer>
+                ),
+                buttonStyle: 'tertiary',
+                onClick: () => {
+                  // @ts-ignore
+                  window.location = '/superset/sqllab';
+                },
               },
-            },
-          ]}
-        />
-        <CardContainer>
-          <SavedQueries user={user} queryFilter={queryFilter} />
-        </CardContainer>
-      </Panel>
-      <Panel header={t('Charts')} key="4">
-        <SubMenu
-          activeChild={chartFilter}
-          name=""
-          // eslint-disable-next-line react/no-children-prop
-          children={[
-            {
-              name: 'Favorite',
-              label: t('Favorite'),
-              onClick: () => setChartFilter('Favorite'),
-            },
-            {
-              name: 'Mine',
-              label: t('Mine'),
-              onClick: () => setChartFilter('Mine'),
-            },
-          ]}
-          buttons={[
-            {
-              name: (
-                <IconContainer>
-                  <Icon name="plus-small" /> Chart{' '}
-                </IconContainer>
-              ),
-              buttonStyle: 'tertiary',
-              onClick: () => {
-                // @ts-ignore
-                window.location = '/chart/add';
+              {
+                name: 'View All',
+                buttonStyle: 'link',
+                onClick: () => {
+                  // @ts-ignore
+                  window.location = '/savedqueryview/list';
+                },
               },
-            },
-            {
-              name: 'View All',
-              buttonStyle: 'link',
-              onClick: () => {
-                // @ts-ignore
-                window.location = '/chart/list';
+            ]}
+          />
+          <CardContainer>
+            <SavedQueries user={user} queryFilter={queryFilter} />
+          </CardContainer>
+        </Panel>
+        <Panel header={t('Charts')} key="4">
+          <SubMenu
+            activeChild={chartFilter}
+            name=""
+            // eslint-disable-next-line react/no-children-prop
+            children={[
+              {
+                name: 'Favorite',
+                label: t('Favorite'),
+                onClick: () => setChartFilter('Favorite'),
+              },
+              {
+                name: 'Mine',
+                label: t('Mine'),
+                onClick: () => setChartFilter('Mine'),
+              },
+            ]}
+            buttons={[
+              {
+                name: (
+                  <IconContainer>
+                    <Icon name="plus-small" /> Chart{' '}
+                  </IconContainer>
+                ),
+                buttonStyle: 'tertiary',
+                onClick: () => {
+                  // @ts-ignore
+                  window.location = '/chart/add';
+                },
               },
-            },
-          ]}
-        />
+              {
+                name: 'View All',
+                buttonStyle: 'link',
+                onClick: () => {
+                  // @ts-ignore
+                  window.location = '/chart/list';
+                },
+              },
+            ]}
+          />
 
-        <CardContainer>
-          <ChartTable chartFilter={chartFilter} user={user} />
-        </CardContainer>
-      </Panel>
-    </Collapse>
+          <CardContainer>
+            <ChartTable chartFilter={chartFilter} user={user} />
+          </CardContainer>
+        </Panel>
+      </Collapse>
+    </WelcomeContainer>
   );
 }


[incubator-superset] 23/33: fix css issues from rebase

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 92541f988df9344e074f4132ce4851772fb23d0f
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Wed Oct 21 15:57:00 2020 -0700

    fix css issues from rebase
---
 superset-frontend/src/views/CRUD/welcome/Welcome.tsx | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index 9e95167..4719468 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -20,7 +20,7 @@ import React from 'react';
 import { styled, t } from '@superset-ui/core';
 import { Collapse } from 'src/common/components';
 import { User } from 'src/types/bootstrapTypes';
-
+import { mq } from '../utils';
 import ActivityTable from './ActivityTable';
 import ChartTable from './ChartTable';
 import SavedQueries from './SavedQueries';
@@ -43,7 +43,10 @@ const WelcomeContainer = styled.div`
       border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
       margin: 0px 26px;
       position: relative;
-      top: -13px;
+      ${[mq[1]]} {
+        margin-top: 5px;
+        margin: 0px 2px;
+      }
     }
     .nav.navbar-nav {
       & > li:nth-child(1),


[incubator-superset] 27/33: Update superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 65b24f11e119e7a86f03f0a5019d9eac7eeef15d
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Fri Oct 23 11:54:57 2020 -0700

    Update superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
    
    Co-authored-by: Evan Rusackas <ev...@preset.io>
---
 superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index 63a1455..ed963f6 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -99,7 +99,7 @@ export default function ActivityTable({ user }: ActivityProps) {
 
   const getIconName = (e: MapProps) => {
     if (e.sql) return 'sql';
-    if (e.url?.indexOf('dashboard') !== -1) {
+    if (e.url?.includes('dashboard')) {
       return 'nav-dashboard';
     }
     if (e.url?.indexOf('explore') !== -1) {


[incubator-superset] 19/33: fix typescript

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 20915ae10287fd336f82c9884fa0de7b5d0bb0b8
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Mon Oct 19 21:40:37 2020 -0700

    fix typescript
---
 .../src/views/CRUD/chart/ChartCard.tsx             |  3 ---
 superset-frontend/src/views/CRUD/utils.tsx         |  2 +-
 .../src/views/CRUD/welcome/ActivityTable.tsx       | 19 ++++++++-------
 .../src/views/CRUD/welcome/SavedQueries.tsx        | 28 ++++++++++++----------
 4 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
index d5cd625..39f94f0 100644
--- a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
+++ b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
@@ -18,7 +18,6 @@
  */
 import React from 'react';
 import { useFavoriteStatus } from 'src/views/CRUD/hooks';
-// import { createFetchRelated, createErrorHandler } from 'src/views/CRUD/utils';
 import { SupersetClient, t } from '@superset-ui/core';
 import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
 import AvatarIcon from 'src/components/AvatarIcon';
@@ -141,8 +140,6 @@ export default function ChartCard({
             fetchFaveStar={fetchFaveStar}
             saveFaveStar={saveFaveStar}
             isStarred={!!favoriteStatus[chart.id]}
-            height={20}
-            width={20}
           />
           <Dropdown overlay={menu}>
             <Icon name="more-horiz" />
diff --git a/superset-frontend/src/views/CRUD/utils.tsx b/superset-frontend/src/views/CRUD/utils.tsx
index 2208cc6..cf4c9ec 100644
--- a/superset-frontend/src/views/CRUD/utils.tsx
+++ b/superset-frontend/src/views/CRUD/utils.tsx
@@ -54,7 +54,7 @@ const createFetchResourceMethod = (method: string) => (
   return [];
 };
 
-export const getBatchData = (userId: string, recent: string) => {
+export const getBatchData = (userId: string | number, recent: string) => {
   const getParams = (filters?: Array<any>) => {
     const params = {
       order_column: 'changed_on_delta_humanized',
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index c9f0883..6c423f0 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -24,7 +24,7 @@ import ListViewCard from 'src/components/ListViewCard';
 import { addDangerToast } from 'src/messageToasts/actions';
 import SubMenu from 'src/components/Menu/SubMenu';
 import { reject } from 'lodash';
-import { getBatchData, createErrorHandler } from '../utils';
+import { getBatchData } from '../utils';
 import EmptyState from './EmptyState';
 
 interface MapProps {
@@ -44,15 +44,15 @@ interface MapProps {
 
 interface ActivityProps {
   user: {
-    userId: string;
+    userId: string | number;
   };
 }
 
 interface ActivityData {
-  Created: Array<object>;
-  Edited: Array<object>;
-  Viewed: Array<object>;
-  Examples: Array<object>;
+  Created?: Array<object>;
+  Edited?: Array<object>;
+  Viewed?: Array<object>;
+  Examples?: Array<object>;
 }
 
 const ActivityContainer = styled.div`
@@ -73,7 +73,7 @@ const ActivityContainer = styled.div`
 `;
 
 export default function ActivityTable({ user }: ActivityProps) {
-  const [activityData, setActivityData] = useState<ActivityData | {}>({});
+  const [activityData, setActivityData] = useState<ActivityData>({});
   const [loading, setLoading] = useState(true);
   const [activeChild, setActiveChild] = useState('Viewed');
   // this api uses log for data which in some cases is can be empty
@@ -143,7 +143,6 @@ export default function ActivityTable({ user }: ActivityProps) {
           Created: [...r.createdByChart, ...r.createdByDash],
           Edited: [...r.editedChart, ...r.editedDash],
         };
-        console.log('r.viewed', r)
         if (r.viewed) {
           const filtered = reject(r.viewed, ['item_url', null]).map(r => r);
           data.Viewed = filtered;
@@ -157,7 +156,9 @@ export default function ActivityTable({ user }: ActivityProps) {
       })
       .catch(e => {
         setLoading(false);
-        addDangerToast(`e ${e}`);
+        addDangerToast(
+          `There was an issue fetching your recent Acitivity: ${e}`,
+        );
       });
   }, []);
 
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
index 5a49ee5..82de6a3 100644
--- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -24,7 +24,6 @@ import { useListViewResource, copyQueryLink } from 'src/views/CRUD/hooks';
 import ListViewCard from 'src/components/ListViewCard';
 import DeleteModal from 'src/components/DeleteModal';
 import Icon from 'src/components/Icon';
-import { SavedQueryObject } from 'src/views/CRUD/types';
 import SubMenu from 'src/components/Menu/SubMenu';
 import EmptyState from './EmptyState';
 
@@ -33,15 +32,15 @@ import { IconContainer, CardContainer, createErrorHandler } from '../utils';
 const PAGE_SIZE = 3;
 
 interface Query {
-  id: number;
-  sql_tables: Array<any>;
-  database: {
+  id?: number;
+  sql_tables?: Array<any>;
+  database?: {
     database_name: string;
   };
-  rows: string;
-  description: string;
-  end_time: string;
-  label: string;
+  rows?: string;
+  description?: string;
+  end_time?: string;
+  label?: string;
 }
 
 interface SavedQueriesProps {
@@ -66,12 +65,12 @@ const SavedQueries = ({
   } = useListViewResource<Query>('saved_query', t('query'), addDangerToast);
   const [queryFilter, setQueryFilter] = useState('Favorite');
   const [queryDeleteModal, setQueryDeleteModal] = useState(false);
-  const [currentlyEdited, setCurrentlyEdited] = useState(null);
+  const [currentlyEdited, setCurrentlyEdited] = useState<Query>({});
 
   const canEdit = hasPerm('can_edit');
   const canDelete = hasPerm('can_delete');
 
-  const handleQueryDelete = ({ id, label }: SavedQueryObject) => {
+  const handleQueryDelete = ({ id, label }: Query) => {
     SupersetClient.delete({
       endpoint: `/api/v1/saved_query/${id}`,
     }).then(
@@ -132,7 +131,10 @@ const SavedQueries = ({
         </Menu.Item>
       )}
       <Menu.Item
-        onClick={() => copyQueryLink(query.id, addDangerToast, addSuccessToast)}
+        onClick={() => {
+          if (query.id)
+            copyQueryLink(query.id, addDangerToast, addSuccessToast);
+        }}
       >
         Share
       </Menu.Item>
@@ -215,8 +217,8 @@ const SavedQueries = ({
               imgURL=""
               title={q.label}
               rows={q.rows}
-              tableName={q.sql_tables[0]?.table}
-              tables={q.sql_tables?.length}
+              tableName={q?.sql_tables && q.sql_tables[0].table}
+              tables={q?.sql_tables?.length}
               loading={loading}
               description={t('Last run ', q.end_time)}
               showImg={false}


[incubator-superset] 17/33: update test part 1

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit aa704b094902dc64c9fed338b9b93274a735f344
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Mon Oct 19 13:54:32 2020 -0700

    update test part 1
---
 .../views/CRUD/welcome/ActivityTable_spec.tsx      |  4 --
 .../views/CRUD/welcome/DashboardTable_spec.tsx     | 15 ++---
 .../{SavedQueries_spec.tsx => EmptyState_spec.tsx} | 36 ++++-------
 .../views/CRUD/welcome/SavedQueries_spec.tsx       | 72 +++++++++++++++++++---
 .../src/views/CRUD/chart/ChartList.tsx             |  4 +-
 superset-frontend/src/views/CRUD/types.ts          |  2 +-
 .../src/views/CRUD/welcome/ActivityTable.tsx       |  1 +
 .../src/views/CRUD/welcome/ChartTable.tsx          |  9 ---
 .../src/views/CRUD/welcome/EmptyState.tsx          | 17 +++--
 .../src/views/CRUD/welcome/SavedQueries.tsx        | 16 ++---
 10 files changed, 104 insertions(+), 72 deletions(-)

diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx
index 2f54d58..19fb397 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx
@@ -79,10 +79,6 @@ describe('ActivityTable', () => {
     expect(wrapper.find(ActivityTable)).toExist();
   });
 
-  it('renders a EmptyState', () => {
-    console.log('wrapper', wrapper.debug())
-  });
-
   it('calls batch method and renders ListViewCArd', () => {
     const chartCall = fetchMock.calls(/chart\/\?q/);
     const dashboardCall = fetchMock.calls(/dashboard\/\?q/);
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx
index dd1e92a..013d3b3 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx
@@ -44,7 +44,6 @@ const mockDashboards = [
 ];
 
 fetchMock.get(dashboardsEndpoint, { result: mockDashboards });
-//fetchMock.get(dashboardsEndpointNoData, { result: [] });
 fetchMock.get(chartsInfoEndpoint, {
   permissions: ['can_list', 'can_edit', 'can_delete'],
 });
@@ -59,8 +58,6 @@ describe('DashboardTable', () => {
   const wrapper = mount(<DashboardTable {...dashboardProps} />, {
     context: { store },
   });
-  // console.log('wrapper', wrapper.debug())
-  // beforeEach(fetchMock.resetHistory);
 
   beforeAll(async () => {
     await waitForComponentToPaint(wrapper);
@@ -68,13 +65,12 @@ describe('DashboardTable', () => {
 
   it('renders', () => {
     expect(wrapper.find(DashboardTable)).toExist();
-    console.log('wrapper', wrapper.debug())
   });
 
   it('render a submenu with clickable tabs and buttons', async () => {
     expect(wrapper.find(SubMenu)).toExist();
     expect(wrapper.find('MenuItem')).toHaveLength(2);
-    expect(wrapper.find('Button')).toHaveLength(2);
+    expect(wrapper.find('Button')).toHaveLength(4);
     act(() => {
       wrapper.find('MenuItem').at(1).simulate('click');
     });
@@ -87,8 +83,9 @@ describe('DashboardTable', () => {
     wrapper.setState({ dashboards: mockDashboards });
     expect(wrapper.find(DashboardCard)).toExist();
   });
-  /*it('display EmptyState if there is no data', ()=>{
-    (const wrapper = mount(<DashboardTable {...dashboardProps} />)
-    console.log('wrapper', wrapper);
-  });*/
+
+  it('display EmptyState if there is no data', () => {
+    fetchMock.resetHistory();
+    expect(wrapper.find('EmptyState')).toExist();
+  });
 });
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx
similarity index 57%
copy from superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
copy to superset-frontend/spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx
index fd367a1..b6e5175 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx
@@ -17,30 +17,20 @@
  * under the License.
  */
 import React from 'react';
-import SavedQueries from 'src/views/CRUD/welcome/SavedQueries';
-import { mount } from 'enzyme';
-import thunk from 'redux-thunk';
+import { styledMount as mount } from 'spec/helpers/theming';
+import { act } from 'react-dom/test-utils';
 
-import configureStore from 'redux-mock-store';
-// store needed for withToasts(DashboardTable)
-const mockStore = configureStore([thunk]);
-const store = mockStore({});
+import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
+import EmptyState from 'src/views/CRUD/welcome/EmptyState';
 
-describe('SavedQueries', () => {
-  const savedQueryProps = {
-    user: {
-      userId: '1',
-    },
-    activityFilter: 'Edit',
-  };
-
-  const wrapper = mount(<SavedQueries {...savedQueryProps} />);
-
-  it('is valid', () => {
-    expect(React.isValidElement(<SavedQueries />)).toBe(true);
+describe('EmptyState', () => {
+  it('it renders an favorite dashboard empty state', () => {
+    const props = {
+      tab: 'Favorite',
+      tableName: 'DASHBOARDS',
+    };
+    const wrapper = mount(<EmptyState {...props} />);
+    expect(wrapper).toExist();
+    console.log('wrapper', wrapper.debug());
   });
- it('takes in props', () => {
-
-    // expect()
-  }); 
 });
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
index fd367a1..28173f2 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
@@ -17,30 +17,84 @@
  * under the License.
  */
 import React from 'react';
-import SavedQueries from 'src/views/CRUD/welcome/SavedQueries';
-import { mount } from 'enzyme';
 import thunk from 'redux-thunk';
-
+import { styledMount as mount } from 'spec/helpers/theming';
+import fetchMock from 'fetch-mock';
 import configureStore from 'redux-mock-store';
+import { act } from 'react-dom/test-utils';
+
+import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
+import SubMenu from 'src/components/Menu/SubMenu';
+import SavedQueries from 'src/views/CRUD/welcome/SavedQueries';
+
 // store needed for withToasts(DashboardTable)
 const mockStore = configureStore([thunk]);
 const store = mockStore({});
 
+const queriesEndpoint = 'glob:*/api/v1/saved_queries/?*';
+
+const mockqueries = [...new Array(3)].map((_, i) => ({
+  created_by: {
+    id: i,
+    first_name: `user`,
+    last_name: `${i}`,
+  },
+  created_on: `${i}-2020`,
+  database: {
+    database_name: `db ${i}`,
+    id: i,
+  },
+  changed_on_delta_humanized: '1 day ago',
+  db_id: i,
+  description: `SQL for ${i}`,
+  id: i,
+  label: `query ${i}`,
+  schema: 'public',
+  sql: `SELECT ${i} FROM table`,
+  sql_tables: [
+    {
+      catalog: null,
+      schema: null,
+      table: `${i}`,
+    },
+  ],
+}));
+
+fetchMock.get(queriesEndpoint, {
+  result: mockqueries,
+});
+
 describe('SavedQueries', () => {
   const savedQueryProps = {
     user: {
       userId: '1',
     },
-    activityFilter: 'Edit',
   };
 
-  const wrapper = mount(<SavedQueries {...savedQueryProps} />);
+  const wrapper = mount(<SavedQueries {...savedQueryProps} />, {
+    context: { store },
+  });
+  beforeAll(async () => {
+    await waitForComponentToPaint(wrapper);
+  });
 
   it('is valid', () => {
-    expect(React.isValidElement(<SavedQueries />)).toBe(true);
+    expect(wrapper.find(SavedQueries)).toExist();
   });
- it('takes in props', () => {
 
-    // expect()
-  }); 
+  it('it renders a submenu with clickable tabls and buttons', async () => {
+    expect(wrapper.find(SubMenu)).toExist();
+    expect(wrapper.find('MenuItem')).toHaveLength(2);
+    expect(wrapper.find('Button')).toHaveLength(4);
+    act(() => {
+      wrapper.find('MenuItem').at(1).simulate('click');
+    });
+    await waitForComponentToPaint(wrapper);
+    expect(fetchMock.calls(/chart\/\?q/)).toHaveLength(1);
+  });
+
+  it('fetches queries favorites and renders chart cards', () => {
+    expect(fetchMock.calls(/chart\/\?q/)).toHaveLength(1);
+    expect(wrapper.find('ListViewCard')).toExist();
+  });
 });
diff --git a/superset-frontend/src/views/CRUD/chart/ChartList.tsx b/superset-frontend/src/views/CRUD/chart/ChartList.tsx
index ec0722e..8a0b8b7 100644
--- a/superset-frontend/src/views/CRUD/chart/ChartList.tsx
+++ b/superset-frontend/src/views/CRUD/chart/ChartList.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import { SupersetClient, getChartMetadataRegistry, t } from '@superset-ui/core';
-import React, { useState, useMemo } from 'react';
+import React, { useMemo } from 'react';
 import rison from 'rison';
 import { uniqBy } from 'lodash';
 import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
@@ -416,7 +416,7 @@ function ChartList(props: ChartListProps) {
         openChartEditModal={openChartEditModal}
         bulkSelectEnabled={bulkSelectEnabled}
         addDangerToast={props.addDangerToast}
-        addSuccessToast={addSuccessToast}
+        addSuccessToast={props.addSuccessToast}
         refreshData={refreshData}
         loading={loading}
       />
diff --git a/superset-frontend/src/views/CRUD/types.ts b/superset-frontend/src/views/CRUD/types.ts
index 1070f57..0946247 100644
--- a/superset-frontend/src/views/CRUD/types.ts
+++ b/superset-frontend/src/views/CRUD/types.ts
@@ -68,4 +68,4 @@ export type SavedQueryObject = {
   schema: string;
   sql: string;
   sql_tables: Array<{ catalog?: string; schema: string; table: string }>;
-};
\ No newline at end of file
+};
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index 02d2cbc..c9f0883 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -143,6 +143,7 @@ export default function ActivityTable({ user }: ActivityProps) {
           Created: [...r.createdByChart, ...r.createdByDash],
           Edited: [...r.editedChart, ...r.editedDash],
         };
+        console.log('r.viewed', r)
         if (r.viewed) {
           const filtered = reject(r.viewed, ['item_url', null]).map(r => r);
           data.Viewed = filtered;
diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
index 99d51c0..c2784d1 100644
--- a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
@@ -77,15 +77,6 @@ function ChartTable({
         value: true,
       });
     }
-    // Do we need search?
-    /* filters.concat([
-      {
-        id: 'dashboard_title',
-        operator: 'ct',
-        value: search,
-      },
-    ]);
-    */
     return filters;
   };
 
diff --git a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
index e1c2936..fe06a18 100644
--- a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
@@ -32,7 +32,7 @@ const Container = styled.div`
   flex-direction: column;
   justify-content: center;
   img {
-    width: 87px;
+    width: 114px;
     display: block;
     margin: 0 auto;
   }
@@ -73,13 +73,19 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
         }}
       >
         <IconContainer>
-          <Icon name="plus-small" /> {tableName}
+          <Icon name="plus-small" />{' '}
+          {tableName === 'SAVED_QUERIES'
+            ? 'SQL LAB QUERY'
+            : tableName
+                .split('')
+                .slice(0, tableName.length - 1)
+                .join('')}{' '}
         </IconContainer>
       </Button>
     </div>
   );
   const span = (
-    <div>
+    <div className="no-recents">
       Recently viewed charts, dashboards, and saved queries will appear here
     </div>
   );
@@ -97,14 +103,15 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
     <Container>
       <img src="/static/assets/images/star-circle.png" alt="star.png" />
       <div>
-        <div>You don't have any favorites yets!</div>
+        <div className="no-favorites">You don't have any favorites yet!</div>
         <Button
           buttonStyle="primary"
           onClick={() => {
             window.location = favRedirects[tableName];
           }}
         >
-          SEE ALL {tableName}
+          SEE ALL{' '}
+          {tableName === 'SAVED_QUERIES' ? 'SQL LAB QUERIES' : tableName }
         </Button>
       </div>
     </Container>
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
index 333d394..5a49ee5 100644
--- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import React, { useEffect, useState } from 'react';
-import { t, styled, SupersetClient } from '@superset-ui/core';
+import { t, SupersetClient } from '@superset-ui/core';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import { Dropdown, Menu } from 'src/common/components';
 import { useListViewResource, copyQueryLink } from 'src/views/CRUD/hooks';
@@ -41,7 +41,6 @@ interface Query {
   rows: string;
   description: string;
   end_time: string;
-  addDangerToast: () => void;
   label: string;
 }
 
@@ -50,15 +49,10 @@ interface SavedQueriesProps {
     userId: string | number;
   };
   queryFilter: string;
+  addDangerToast: (arg0: string) => void;
+  addSuccessToast: (arg0: string) => void;
 }
 
-const NoData = styled.div`
-  .create-your-query {
-    display: block;
-    margin: 0 auto;
-  }
-`;
-
 const SavedQueries = ({
   user,
   addDangerToast,
@@ -139,7 +133,9 @@ const SavedQueries = ({
       )}
       <Menu.Item
         onClick={() => copyQueryLink(query.id, addDangerToast, addSuccessToast)}
-      >Share</Menu.Item>
+      >
+        Share
+      </Menu.Item>
       {canDelete && (
         <Menu.Item
           onClick={() => {


[incubator-superset] 31/33: fix pesky type errors

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 46b9cf438195e216161ae170aaf745f2002e42eb
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Sun Oct 25 22:25:03 2020 -0700

    fix pesky type errors
---
 superset-frontend/src/components/ListViewCard/index.tsx | 4 ++--
 superset-frontend/src/components/Menu/SubMenu.tsx       | 2 +-
 superset-frontend/src/views/CRUD/hooks.ts               | 3 +--
 3 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/superset-frontend/src/components/ListViewCard/index.tsx b/superset-frontend/src/components/ListViewCard/index.tsx
index 02f60f6..b4350bd 100644
--- a/superset-frontend/src/components/ListViewCard/index.tsx
+++ b/superset-frontend/src/components/ListViewCard/index.tsx
@@ -202,8 +202,8 @@ function ListViewCard({
             <a href={url}>
               <div className="gradient-container">
                 <ImageLoader
-                  src={imgURL}
-                  fallback={imgFallbackURL}
+                  src={imgURL || ''}
+                  fallback={imgFallbackURL || ''}
                   isLoading={loading}
                   position={imgPosition}
                 />
diff --git a/superset-frontend/src/components/Menu/SubMenu.tsx b/superset-frontend/src/components/Menu/SubMenu.tsx
index f58c2bd..db9767b 100644
--- a/superset-frontend/src/components/Menu/SubMenu.tsx
+++ b/superset-frontend/src/components/Menu/SubMenu.tsx
@@ -131,7 +131,7 @@ const SubMenu: React.FunctionComponent<SubMenuProps> = props => {
                     key={`${tab.label}`}
                   >
                     <div>
-                      <Link to={tab.url}>{tab.label}</Link>
+                      <Link to={tab.url || ''}>{tab.label}</Link>
                     </div>
                   </li>
                 );
diff --git a/superset-frontend/src/views/CRUD/hooks.ts b/superset-frontend/src/views/CRUD/hooks.ts
index 1261a5e..b083bde 100644
--- a/superset-frontend/src/views/CRUD/hooks.ts
+++ b/superset-frontend/src/views/CRUD/hooks.ts
@@ -21,9 +21,8 @@ import { useState, useEffect, useCallback, useRef } from 'react';
 import { SupersetClient, t } from '@superset-ui/core';
 
 import { createErrorHandler } from 'src/views/CRUD/utils';
-import { Slice } from 'src/explore/components/PropertiesModal';
 import { FetchDataConfig } from 'src/components/ListView';
-import Chart from 'src/types/Chart';
+import Chart, { Slice } from 'src/types/Chart';
 import { FavoriteStatus } from './types';
 
 interface ListViewResourceState<D extends object = any> {


[incubator-superset] 04/33: step 1: broken stuff!

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 64d806cea34487b31e281b95840eb6614eadb806
Author: Evan Rusackas <ev...@preset.io>
AuthorDate: Thu Sep 10 13:57:48 2020 -0700

    step 1: broken stuff!
---
 superset-frontend/src/components/Menu/SubMenu.tsx    |  5 +++++
 superset-frontend/src/views/CRUD/welcome/Welcome.tsx | 15 ++++-----------
 2 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/superset-frontend/src/components/Menu/SubMenu.tsx b/superset-frontend/src/components/Menu/SubMenu.tsx
index df7e501..381ac2e 100644
--- a/superset-frontend/src/components/Menu/SubMenu.tsx
+++ b/superset-frontend/src/components/Menu/SubMenu.tsx
@@ -66,8 +66,13 @@ type MenuClickHandler = React.MouseEventHandler<MenuItem>;
 type MenuChild = {
   label: string;
   name: string;
+<<<<<<< HEAD
   url: string;
   usesRouter?: boolean;
+=======
+  url?: string;
+  onClick?: any;
+>>>>>>> step 1: broken stuff!
 };
 
 export interface ButtonProps {
diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index 337b4e1..a610d43 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -16,21 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React, { useCallback, useState } from 'react';
-import {
-  Panel,
-  Row,
-  Col,
-  Tabs,
-  Tab,
-  FormControl,
-  FormControlProps,
-} from 'react-bootstrap';
+import React, { useState } from 'react';
+import { FormControl } from 'react-bootstrap';
+import SubMenu from 'src/components/Menu/SubMenu';
+import { Collapse } from 'src/common/components';
 import { t } from '@superset-ui/core';
 import { useQueryParam, StringParam, QueryParamConfig } from 'use-query-params';
 import { User } from 'src/types/bootstrapTypes';
 import RecentActivity from 'src/profile/components/RecentActivity';
-import Favorites from 'src/profile/components/Favorites';
 import ChartTable from './ChartTable';
 import SavedQueries from './SavedQueries';
 import DashboardTable from './DashboardTable';


[incubator-superset] 22/33: update chart card

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit b7e35f92b38990cd6c0da5bb0ed1e09b433024cd
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Wed Oct 21 15:35:34 2020 -0700

    update chart card
---
 superset-frontend/src/views/CRUD/chart/ChartCard.tsx | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
index 39f94f0..05872a7 100644
--- a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
+++ b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
@@ -20,13 +20,14 @@ import React from 'react';
 import { useFavoriteStatus } from 'src/views/CRUD/hooks';
 import { SupersetClient, t } from '@superset-ui/core';
 import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
-import AvatarIcon from 'src/components/AvatarIcon';
 import Icon from 'src/components/Icon';
 import Chart from 'src/types/Chart';
+
 import ListViewCard from 'src/components/ListViewCard';
 import Label from 'src/components/Label';
 import { Dropdown, Menu } from 'src/common/components';
 import FaveStar from 'src/components/FaveStar';
+import FacePile from 'src/components/FacePile';
 
 const FAVESTAR_BASE_URL = '/superset/favstar/slice';
 
@@ -120,16 +121,7 @@ export default function ChartCard({
       imgURL={chart.thumbnail_url ?? ''}
       imgFallbackURL="/static/assets/images/chart-card-fallback.png"
       description={t('Last modified %s', chart.changed_on_delta_humanized)}
-      coverLeft={(chart.owners || []).slice(0, 5).map(owner => (
-        <AvatarIcon
-          key={owner.id}
-          uniqueKey={`${owner.username}-${chart.id}`}
-          firstName={owner.first_name}
-          lastName={owner.last_name}
-          iconSize={24}
-          textSize={9}
-        />
-      ))}
+      coverLeft={<FacePile users={chart.owners || []} />}
       coverRight={
         <Label bsStyle="secondary">{chart.datasource_name_text}</Label>
       }


[incubator-superset] 20/33: add images and update emptystate

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit a363a0fba88bf98596b80acdcc1a20f6367ad2db
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Wed Oct 21 08:47:07 2020 -0700

    add images and update emptystate
---
 superset-frontend/images/empty-charts.png          | Bin 0 -> 2274 bytes
 superset-frontend/images/empty-dashboard.png       | Bin 0 -> 1467 bytes
 superset-frontend/images/empty-queries.png         | Bin 0 -> 1791 bytes
 superset-frontend/images/union.png                 | Bin 1131 -> 1694 bytes
 .../src/views/CRUD/welcome/ActivityTable.tsx       |   5 ++--
 .../src/views/CRUD/welcome/EmptyState.tsx          |  30 +++++++++++++++------
 .../src/views/CRUD/welcome/Welcome.tsx             |   1 -
 7 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/superset-frontend/images/empty-charts.png b/superset-frontend/images/empty-charts.png
new file mode 100644
index 0000000..b814d35
Binary files /dev/null and b/superset-frontend/images/empty-charts.png differ
diff --git a/superset-frontend/images/empty-dashboard.png b/superset-frontend/images/empty-dashboard.png
new file mode 100644
index 0000000..b0d4462
Binary files /dev/null and b/superset-frontend/images/empty-dashboard.png differ
diff --git a/superset-frontend/images/empty-queries.png b/superset-frontend/images/empty-queries.png
new file mode 100644
index 0000000..adc51c0
Binary files /dev/null and b/superset-frontend/images/empty-queries.png differ
diff --git a/superset-frontend/images/union.png b/superset-frontend/images/union.png
index ca1dbe5..af94c07 100644
Binary files a/superset-frontend/images/union.png and b/superset-frontend/images/union.png differ
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index 6c423f0..31dce1c 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -76,7 +76,7 @@ export default function ActivityTable({ user }: ActivityProps) {
   const [activityData, setActivityData] = useState<ActivityData>({});
   const [loading, setLoading] = useState(true);
   const [activeChild, setActiveChild] = useState('Viewed');
-  // this api uses log for data which in some cases is can be empty
+  // this api uses log for data which in some cases can be empty
   const recent = `/superset/recent_activity/${user.userId}/?limit=5`;
 
   const getFilterTitle = (e: MapProps) => {
@@ -181,6 +181,7 @@ export default function ActivityTable({ user }: ActivityProps) {
     ));
   };
   if (loading) return <>loading ...</>;
+  console.log('activity', activeChild)
   return (
     <>
       <>
@@ -194,7 +195,7 @@ export default function ActivityTable({ user }: ActivityProps) {
           {activityData[activeChild]?.length > 0 ? (
             <ActivityContainer>{renderActivity()}</ActivityContainer>
           ) : (
-            <EmptyState tableName="RECENTS" tab="Mine" />
+            <EmptyState tableName="RECENTS" tab={activeChild} />
           )}
         </>
       </>
diff --git a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
index fe06a18..00aff9b 100644
--- a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
@@ -63,9 +63,19 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
     CHARTS: '/chart/list',
     SAVED_QUERIES: '/savedqueryview/list/',
   };
-  const Mine = (
+  const tableIcon = {
+    RECENTS: 'union.png',
+    DASHBOARDS: 'empty-dashboard.png',
+    CHARTS: 'empty-charts.png',
+    SAVED_QUERIES: 'empty-queries.png',
+  };
+  const mine = (
     <div>
-      <div>{`No ${tableName.toLowerCase()} yet`}</div>
+      <div>{`No ${
+        tableName === 'SAVED_QUERIES'
+          ? 'saved queries'
+          : tableName.toLowerCase()
+      } yet`}</div>
       <Button
         buttonStyle="primary"
         onClick={() => {
@@ -75,7 +85,7 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
         <IconContainer>
           <Icon name="plus-small" />{' '}
           {tableName === 'SAVED_QUERIES'
-            ? 'SQL LAB QUERY'
+            ? 'SQL QUERY'
             : tableName
                 .split('')
                 .slice(0, tableName.length - 1)
@@ -86,15 +96,19 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
   );
   const span = (
     <div className="no-recents">
-      Recently viewed charts, dashboards, and saved queries will appear here
+      Recently {tab?.toLowerCase()} charts, dashboards, and saved queries will
+      appear here
     </div>
   );
 
-  if (tab === 'Mine') {
+  if (tab === 'Mine' || tableName === 'RECENTS') {
     return (
       <Container>
-        <img src="/static/assets/images/union.png" alt="union.png" />
-        {tableName === 'RECENTS' ? span : Mine}
+        <img
+          src={`/static/assets/images/${tableIcon[tableName]}`}
+          alt={`${tableName}`}
+        />
+        {tableName === 'RECENTS' ? span : mine}
       </Container>
     );
   }
@@ -111,7 +125,7 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
           }}
         >
           SEE ALL{' '}
-          {tableName === 'SAVED_QUERIES' ? 'SQL LAB QUERIES' : tableName }
+          {tableName === 'SAVED_QUERIES' ? 'SQL LAB QUERIES' : tableName}
         </Button>
       </div>
     </Container>
diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index 7f7464b..9e95167 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -20,7 +20,6 @@ import React from 'react';
 import { styled, t } from '@superset-ui/core';
 import { Collapse } from 'src/common/components';
 import { User } from 'src/types/bootstrapTypes';
-import Icon from 'src/components/Icon';
 
 import ActivityTable from './ActivityTable';
 import ChartTable from './ChartTable';


[incubator-superset] 16/33: update branch

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 1b46423c112742728919bb91e1430147e7384ee8
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Mon Oct 19 07:28:25 2020 -0700

    update branch
---
 superset-frontend/images/star-circle.png           | Bin 0 -> 2705 bytes
 superset-frontend/images/union.png                 | Bin 0 -> 1131 bytes
 .../views/CRUD/chart/ChartList_spec.jsx            |   1 +
 .../views/CRUD/welcome/ActivityTable_spec.tsx      |  93 +++++++++
 .../views/CRUD/welcome/ChartTable_spec.tsx         |   1 -
 .../views/CRUD/welcome/DashboardTable_spec.tsx     |  78 +++++---
 .../views/CRUD/welcome/SavedQueries_spec.tsx       |  30 ++-
 .../views/CRUD/welcome/Welcome_spec.tsx            |  14 +-
 .../src/components/ListViewCard/index.tsx          |  31 ++-
 .../src/views/CRUD/chart/ChartCard.tsx             |  13 +-
 .../src/views/CRUD/dashboard/DashboardCard.tsx     |  13 +-
 .../views/CRUD/data/savedquery/SavedQueryList.tsx  |  15 +-
 superset-frontend/src/views/CRUD/hooks.ts          |  49 ++++-
 superset-frontend/src/views/CRUD/types.ts          |  15 +-
 superset-frontend/src/views/CRUD/utils.tsx         | 130 +++++++++++--
 .../src/views/CRUD/welcome/ActivityTable.tsx       | 181 +++++++++++------
 .../src/views/CRUD/welcome/ChartTable.tsx          |  81 ++++++--
 .../src/views/CRUD/welcome/DashboardTable.tsx      | 148 ++++++++++----
 .../src/views/CRUD/welcome/EmptyState.tsx          | 112 +++++++++++
 .../src/views/CRUD/welcome/SavedQueries.tsx        | 177 ++++++++++++++---
 .../src/views/CRUD/welcome/Welcome.tsx             | 215 +++------------------
 21 files changed, 974 insertions(+), 423 deletions(-)

diff --git a/superset-frontend/images/star-circle.png b/superset-frontend/images/star-circle.png
new file mode 100644
index 0000000..77fd94d
Binary files /dev/null and b/superset-frontend/images/star-circle.png differ
diff --git a/superset-frontend/images/union.png b/superset-frontend/images/union.png
new file mode 100644
index 0000000..ca1dbe5
Binary files /dev/null and b/superset-frontend/images/union.png differ
diff --git a/superset-frontend/spec/javascripts/views/CRUD/chart/ChartList_spec.jsx b/superset-frontend/spec/javascripts/views/CRUD/chart/ChartList_spec.jsx
index 3872358..3abedc3 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/chart/ChartList_spec.jsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/chart/ChartList_spec.jsx
@@ -54,6 +54,7 @@ const mockCharts = [...new Array(3)].map((_, i) => ({
 fetchMock.get(chartsInfoEndpoint, {
   permissions: ['can_list', 'can_edit', 'can_delete'],
 });
+
 fetchMock.get(chartssOwnersEndpoint, {
   result: [],
 });
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx
new file mode 100644
index 0000000..2f54d58
--- /dev/null
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx
@@ -0,0 +1,93 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import { styledMount as mount } from 'spec/helpers/theming';
+import thunk from 'redux-thunk';
+import fetchMock from 'fetch-mock';
+
+import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
+import configureStore from 'redux-mock-store';
+import ActivityTable from 'src/views/CRUD/welcome/ActivityTable';
+import ListViewCard from 'src/components/ListViewCard';
+
+const mockStore = configureStore([thunk]);
+const store = mockStore({});
+
+const chartsEndpoint = 'glob:*/api/v1/chart/?*';
+const dashboardEndpoint = 'glob:*/api/v1/dashboard/?*';
+const savedQueryEndpoint = 'glob:*/api/v1/saved_query/?*';
+
+fetchMock.get(chartsEndpoint, {
+  result: [
+    {
+      slice_name: 'ChartyChart',
+      changed_on_utc: '24 Feb 2014 10:13:14',
+      url: '/fakeUrl/explore',
+      id: '4',
+      table: {},
+    },
+  ],
+});
+
+fetchMock.get(dashboardEndpoint, {
+  result: [
+    {
+      dashboard_title: 'Dashboard_Test',
+      changed_on_utc: '24 Feb 2014 10:13:14',
+      url: '/fakeUrl/dashboard',
+      id: '3',
+    },
+  ],
+});
+
+fetchMock.get(savedQueryEndpoint, {
+  result: [],
+});
+
+describe('ActivityTable', () => {
+  const activityProps = {
+    user: {
+      userId: '1',
+    },
+    activityFilter: 'Edited',
+  };
+  const wrapper = mount(<ActivityTable {...activityProps} />, {
+    context: { store },
+  });
+
+  beforeAll(async () => {
+    await waitForComponentToPaint(wrapper);
+  });
+
+  it('renders', () => {
+    expect(wrapper.find(ActivityTable)).toExist();
+  });
+
+  it('renders a EmptyState', () => {
+    console.log('wrapper', wrapper.debug())
+  });
+
+  it('calls batch method and renders ListViewCArd', () => {
+    const chartCall = fetchMock.calls(/chart\/\?q/);
+    const dashboardCall = fetchMock.calls(/dashboard\/\?q/);
+    expect(chartCall).toHaveLength(2);
+    expect(dashboardCall).toHaveLength(2);
+    expect(wrapper.find(ListViewCard)).toHaveLength(2);
+  });
+});
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx
index ed0666e..a631a78 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx
@@ -21,7 +21,6 @@ import { mount } from 'enzyme';
 import thunk from 'redux-thunk';
 import fetchMock from 'fetch-mock';
 
-import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
 import configureStore from 'redux-mock-store';
 import ChartTable from 'src/views/CRUD/welcome/ChartTable';
 import ChartCard from 'src/views/CRUD/chart/ChartCard';
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx
index 4d2eb9e..dd1e92a 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx
@@ -17,48 +17,78 @@
  * under the License.
  */
 import React from 'react';
-import { mount } from 'enzyme';
+import { styledMount as mount } from 'spec/helpers/theming';
 import thunk from 'redux-thunk';
 import configureStore from 'redux-mock-store';
 import fetchMock from 'fetch-mock';
-import { supersetTheme, ThemeProvider } from '@superset-ui/core';
+import { act } from 'react-dom/test-utils';
 
+import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
+import SubMenu from 'src/components/Menu/SubMenu';
 import DashboardTable from 'src/views/CRUD/welcome/DashboardTable';
-import DashboardCard from 'src/views/CRUD/welcome/DashboardCard';
+import DashboardCard from 'src/views/CRUD/dashboard/DashboardCard';
 
 // store needed for withToasts(DashboardTable)
 const mockStore = configureStore([thunk]);
 const store = mockStore({});
 
-const dashboardsEndpoint = 'glob:*/api/v1/dashboard/*';
-const mockDashboards = [{ id: 1, url: 'url', dashboard_title: 'title' }];
+const dashboardsEndpoint = 'glob:*/api/v1/dashboard/?*';
+const chartsInfoEndpoint = 'glob:*/api/v1/chart/_info*';
+const mockDashboards = [
+  {
+    id: 1,
+    url: 'url',
+    dashboard_title: 'title',
+    changed_on_utc: '24 Feb 2014 10:13:14',
+  },
+];
 
 fetchMock.get(dashboardsEndpoint, { result: mockDashboards });
+//fetchMock.get(dashboardsEndpointNoData, { result: [] });
+fetchMock.get(chartsInfoEndpoint, {
+  permissions: ['can_list', 'can_edit', 'can_delete'],
+});
 
-function setup() {
-  // use mount because data fetching is triggered on mount
-  return mount(<DashboardTable />, {
+describe('DashboardTable', () => {
+  const dashboardProps = {
+    dashboardFilter: 'Favorite',
+    user: {
+      userId: '2',
+    },
+  };
+  const wrapper = mount(<DashboardTable {...dashboardProps} />, {
     context: { store },
-    wrappingComponent: ThemeProvider,
-    wrappingComponentProps: { theme: supersetTheme },
   });
-}
+  // console.log('wrapper', wrapper.debug())
+  // beforeEach(fetchMock.resetHistory);
 
-describe('DashboardTable', () => {
-  beforeEach(fetchMock.resetHistory);
+  beforeAll(async () => {
+    await waitForComponentToPaint(wrapper);
+  });
 
-  it('fetches dashboards and renders a ', () => {
-    return new Promise(done => {
-      const wrapper = setup();
+  it('renders', () => {
+    expect(wrapper.find(DashboardTable)).toExist();
+    console.log('wrapper', wrapper.debug())
+  });
 
-      setTimeout(() => {
-        expect(fetchMock.calls(dashboardsEndpoint)).toHaveLength(1);
-        // there's a delay between response and updating state, so manually set it
-        // rather than adding a timeout which could introduce flakiness
-        wrapper.setState({ dashboards: mockDashboards });
-        expect(wrapper.find(DashboardCard)).toExist();
-        done();
-      });
+  it('render a submenu with clickable tabs and buttons', async () => {
+    expect(wrapper.find(SubMenu)).toExist();
+    expect(wrapper.find('MenuItem')).toHaveLength(2);
+    expect(wrapper.find('Button')).toHaveLength(2);
+    act(() => {
+      wrapper.find('MenuItem').at(1).simulate('click');
     });
+    await waitForComponentToPaint(wrapper);
+    expect(fetchMock.calls(/dashboard\/\?q/)).toHaveLength(1);
+  });
+
+  it('fetches dashboards and renders a card', () => {
+    expect(fetchMock.calls(/dashboard\/\?q/)).toHaveLength(1);
+    wrapper.setState({ dashboards: mockDashboards });
+    expect(wrapper.find(DashboardCard)).toExist();
   });
+  /*it('display EmptyState if there is no data', ()=>{
+    (const wrapper = mount(<DashboardTable {...dashboardProps} />)
+    console.log('wrapper', wrapper);
+  });*/
 });
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
index d5c61b5..fd367a1 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
@@ -17,16 +17,30 @@
  * under the License.
  */
 import React from 'react';
-import { SavedQueries } from 'src/views/CRUD/welcome/SavedQueries'
-import { shallow } from 'enzyme';
+import SavedQueries from 'src/views/CRUD/welcome/SavedQueries';
+import { mount } from 'enzyme';
+import thunk from 'redux-thunk';
+
+import configureStore from 'redux-mock-store';
+// store needed for withToasts(DashboardTable)
+const mockStore = configureStore([thunk]);
+const store = mockStore({});
 
 describe('SavedQueries', () => {
-  const 
+  const savedQueryProps = {
+    user: {
+      userId: '1',
+    },
+    activityFilter: 'Edit',
+  };
+
+  const wrapper = mount(<SavedQueries {...savedQueryProps} />);
+
   it('is valid', () => {
     expect(React.isValidElement(<SavedQueries />)).toBe(true);
   });
-  it('SaveQueries renders up to three saved queries', ()=>{
-    //expect() 
-  })
-  
-});
\ No newline at end of file
+ it('takes in props', () => {
+
+    // expect()
+  }); 
+});
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
index 13c27b7..38891c0 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import React from 'react';
-import { shallow } from 'enzyme';
+import { styledMount as mount } from 'spec/helpers/theming';
 
 import Welcome from 'src/views/CRUD/welcome/Welcome';
 
@@ -33,12 +33,12 @@ describe('Welcome', () => {
       isActive: true,
     },
   };
-  it('is valid', () => {
-    expect(React.isValidElement(<Welcome {...mockedProps} />)).toBe(true);
+  const wrapper = mount(<Welcome {...mockedProps} />);
+  it('is renders', () => {
+    expect(wrapper.find(Welcome)).toExist();
   });
-  it('renders 3 submenu components', () => {
-    const wrapper = shallow(<Welcome {...mockedProps} />);
-    expect(wrapper.find('SubMenu')).toHaveLength(4);
+  it('renders first submenu on page load', () => {
+    expect(wrapper.find('SubMenu')).toHaveLength(1);
+    expect(wrapper.find('PanelContent')).toHaveLength(4);
   });
-  console.log('wrapper', )
 });
diff --git a/superset-frontend/src/components/ListViewCard/index.tsx b/superset-frontend/src/components/ListViewCard/index.tsx
index 427beba..674985b 100644
--- a/superset-frontend/src/components/ListViewCard/index.tsx
+++ b/superset-frontend/src/components/ListViewCard/index.tsx
@@ -139,11 +139,26 @@ const SkeletonActions = styled(Skeleton.Button)`
   width: ${({ theme }) => theme.gridUnit * 10}px;
 `;
 
+const QueryData = styled.div`
+  display: flex;
+  flex-direction: row;
+  justify-content: flex-start;
+  border-bottom: 1px solid #e0e0e0;
+  .title {
+    font-weight: 500;
+    color: #b2b2b2;
+  }
+  .holder {
+    margin: 10px 10px 10px 10px;
+  }
+`;
+
 const paragraphConfig = { rows: 1, width: 150 };
 interface CardProps {
   title: React.ReactNode;
   url?: string;
   imgURL: string;
+  tables?: string | number;
   imgFallbackURL: string;
   imgPosition?: BackgroundPosition;
   description: string;
@@ -167,6 +182,7 @@ function ListViewCard({
   imgFallbackURL,
   description,
   coverLeft,
+  tables,
   coverRight,
   actions,
   avatar,
@@ -174,7 +190,6 @@ function ListViewCard({
   loading,
   imgPosition = 'top',
   showImg = true,
-  rows,
   isRecent,
 }: CardProps) {
   return (
@@ -205,16 +220,12 @@ function ListViewCard({
           </Cover>
         ) : (
           <QueryData>
-            <div>
-              <div>Tables</div>
-              <div>{}</div>
-            </div>
-            <div>
-              <div>Rows</div>
-              <div>{rows}</div>
+            <div className="holder">
+              <div className="title">Tables</div>
+              <div>{tables}</div>
             </div>
-            <div>
-              <div>Datasource Name</div>
+            <div className="holder">
+              <div className="title">Datasource Name</div>
               <div>{tableName}</div>
             </div>
           </QueryData>
diff --git a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
index e1ab61f..d5cd625 100644
--- a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
+++ b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
@@ -54,11 +54,12 @@ export default function ChartCard({
 }: ChartCardProps) {
   const canEdit = hasPerm('can_edit');
   const canDelete = hasPerm('can_delete');
-  const [favoriteStatusRef, fetchFaveStar, saveFaveStar] = useFavoriteStatus(
-    {},
-    FAVESTAR_BASE_URL,
-    addDangerToast,
-  );
+  const [
+    favoriteStatusRef,
+    fetchFaveStar,
+    saveFaveStar,
+    favoriteStatus,
+  ] = useFavoriteStatus({}, FAVESTAR_BASE_URL, addDangerToast);
 
   function handleChartDelete({ id, slice_name: sliceName }: Chart) {
     SupersetClient.delete({
@@ -139,7 +140,7 @@ export default function ChartCard({
             itemId={chart.id}
             fetchFaveStar={fetchFaveStar}
             saveFaveStar={saveFaveStar}
-            isStarred={!!favoriteStatusRef.current[chart.id]}
+            isStarred={!!favoriteStatus[chart.id]}
             height={20}
             width={20}
           />
diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
index e2e3b1f..2a09f70 100644
--- a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
+++ b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
@@ -27,11 +27,12 @@ function DashboardCard({
   const canEdit = hasPerm('can_edit');
   const canDelete = hasPerm('can_delete');
   const canExport = hasPerm('can_mulexport');
-  const [favoriteStatusRef, fetchFaveStar, saveFaveStar] = useFavoriteStatus(
-    {},
-    FAVESTAR_BASE_URL,
-    addDangerToast,
-  );
+  const [
+    favoriteStatusRef,
+    fetchFaveStar,
+    saveFaveStar,
+    favoriteStatus,
+  ] = useFavoriteStatus({}, FAVESTAR_BASE_URL, addDangerToast);
 
   function handleDashboardDelete({
     id,
@@ -126,7 +127,7 @@ function DashboardCard({
             itemId={dashboard.id}
             fetchFaveStar={fetchFaveStar}
             saveFaveStar={saveFaveStar}
-            isStarred={!!favoriteStatusRef.current[dashboard.id]}
+            isStarred={!!favoriteStatus[dashboard.id]}
           />
           <Dropdown overlay={menu}>
             <Icon name="more-horiz" />
diff --git a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
index f887af5..bfb4719 100644
--- a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
+++ b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
@@ -39,6 +39,7 @@ import DeleteModal from 'src/components/DeleteModal';
 import ActionsBar, { ActionProps } from 'src/components/ListView/ActionsBar';
 import { IconName } from 'src/components/Icon';
 import { commonMenuData } from 'src/views/CRUD/data/common';
+import { SavedQueryObject } from 'src/views/CRUD/types';
 import SavedQueryPreviewModal from './SavedQueryPreviewModal';
 
 const PAGE_SIZE = 25;
@@ -48,20 +49,6 @@ interface SavedQueryListProps {
   addSuccessToast: (msg: string) => void;
 }
 
-type SavedQueryObject = {
-  database: {
-    database_name: string;
-    id: number;
-  };
-  db_id: number;
-  description?: string;
-  id: number;
-  label: string;
-  schema: string;
-  sql: string;
-  sql_tables: Array<{ catalog?: string; schema: string; table: string }>;
-};
-
 const StyledTableLabel = styled.div`
   .count {
     margin-left: 5px;
diff --git a/superset-frontend/src/views/CRUD/hooks.ts b/superset-frontend/src/views/CRUD/hooks.ts
index 80de0a7..9dbdbc2 100644
--- a/superset-frontend/src/views/CRUD/hooks.ts
+++ b/superset-frontend/src/views/CRUD/hooks.ts
@@ -330,6 +330,7 @@ export function useFavoriteStatus(
       endpoint: `${baseURL}/${id}/count/`,
     }).then(
       ({ json }) => {
+        console.log('json', json);
         updateFavoriteStatus({ [id]: json.count > 0 });
       },
       createErrorHandler(errMsg =>
@@ -357,7 +358,12 @@ export function useFavoriteStatus(
     );
   };
 
-  return [favoriteStatusRef, fetchFaveStar, saveFaveStar] as const;
+  return [
+    favoriteStatusRef,
+    fetchFaveStar,
+    saveFaveStar,
+    favoriteStatus,
+  ] as const;
 }
 
 export const useChartEditModal = (
@@ -397,3 +403,44 @@ export const useChartEditModal = (
     closeChartEditModal,
   };
 };
+
+export const copyQueryLink = (
+  id: number,
+  addDangerToast: (arg0: string) => void,
+  addSuccessToast: (arg0: string) => void,
+) => {
+  const selection: Selection | null = document.getSelection();
+
+  if (selection) {
+    selection.removeAllRanges();
+    const range = document.createRange();
+    const span = document.createElement('span');
+    span.textContent = `${window.location.origin}/superset/sqllab?savedQueryId=${id}`;
+    span.style.position = 'fixed';
+    span.style.top = '0';
+    span.style.clip = 'rect(0, 0, 0, 0)';
+    span.style.whiteSpace = 'pre';
+
+    document.body.appendChild(span);
+    range.selectNode(span);
+    selection.addRange(range);
+
+    try {
+      if (!document.execCommand('copy')) {
+        throw new Error(t('Not successful'));
+      }
+    } catch (err) {
+      addDangerToast(t('Sorry, your browser does not support copying.'));
+    }
+
+    document.body.removeChild(span);
+    if (selection.removeRange) {
+      selection.removeRange(range);
+    } else {
+      selection.removeAllRanges();
+    }
+    console.log('-----Success Toast--------')
+    addSuccessToast(t('Link Copied!'));
+    console.log('-----Success Toast--------')
+  }
+};
diff --git a/superset-frontend/src/views/CRUD/types.ts b/superset-frontend/src/views/CRUD/types.ts
index afd1956..1070f57 100644
--- a/superset-frontend/src/views/CRUD/types.ts
+++ b/superset-frontend/src/views/CRUD/types.ts
@@ -27,7 +27,6 @@ export interface DashboardTableProps {
   addDangerToast: (message: string) => void;
   addSuccessToast: (message: string) => void;
   search: string;
-  dashboardFilter?: string;
   user?: User;
 }
 
@@ -56,3 +55,17 @@ export interface DashboardCardProps {
   addSuccessToast: (msg: string) => void;
   openDashboardEditModal?: (d: Dashboard) => void;
 }
+
+export type SavedQueryObject = {
+  database: {
+    database_name: string;
+    id: number;
+  };
+  db_id: number;
+  description?: string;
+  id: number;
+  label: string;
+  schema: string;
+  sql: string;
+  sql_tables: Array<{ catalog?: string; schema: string; table: string }>;
+};
\ No newline at end of file
diff --git a/superset-frontend/src/views/CRUD/utils.tsx b/superset-frontend/src/views/CRUD/utils.tsx
index 6caabb9..2208cc6 100644
--- a/superset-frontend/src/views/CRUD/utils.tsx
+++ b/superset-frontend/src/views/CRUD/utils.tsx
@@ -20,6 +20,7 @@ import {
   SupersetClient,
   SupersetClientResponse,
   logging,
+  styled,
 } from '@superset-ui/core';
 import rison from 'rison';
 import getClientErrorObject from 'src/utils/getClientErrorObject';
@@ -53,27 +54,82 @@ const createFetchResourceMethod = (method: string) => (
   return [];
 };
 
-export const createBatchMethod = (queryParams: string, created?: string) => {
+export const getBatchData = (userId: string, recent: string) => {
+  const getParams = (filters?: Array<any>) => {
+    const params = {
+      order_column: 'changed_on_delta_humanized',
+      order_direction: 'desc',
+      page: 0,
+      page_size: 3,
+      filters,
+    };
+    if (!filters) delete params.filters;
+    return rison.encode(params);
+  };
+  const filters = {
+    // chart and dashbaord uses same filters
+    // for edited and created
+    edited: [
+      {
+        col: 'changed_by',
+        opr: 'rel_o_m',
+        value: `${userId}`,
+      },
+    ],
+    created: [
+      {
+        col: 'created_by',
+        opr: 'rel_o_m',
+        value: `${userId}`,
+      },
+    ],
+  };
   const baseBatch = [
-    SupersetClient.get({ endpoint: `/api/v1/dashboard/?q=${queryParams}` }),
-    SupersetClient.get({ endpoint: `/api/v1/chart/?q=${queryParams}` }),
+    SupersetClient.get({ endpoint: recent }),
+    SupersetClient.get({
+      endpoint: `/api/v1/dashboard/?q=${getParams(filters.edited)}`,
+    }),
+    SupersetClient.get({
+      endpoint: `/api/v1/chart/?q=${getParams(filters.edited)}`,
+    }),
+    SupersetClient.get({
+      endpoint: `/api/v1/dashboard/?q=${getParams(filters.created)}`,
+    }),
+    SupersetClient.get({
+      endpoint: `/api/v1/chart/?q=${getParams(filters.created)}`,
+    }),
   ];
-  if (created)
-    baseBatch.push(
-      SupersetClient.get({ endpoint: `/api/v1/saved_query/?q=${queryParams}` }),
-    );
-  return Promise.all(baseBatch).then(([dashboardRes, chartRes, savedQuery]) => {
-    const results = [];
-    const ifQuery = savedQuery ? savedQuery.json?.result.slice(0, 3) : [];
-    results.push(
-      ...[
-        ...dashboardRes.json?.result.slice(0, 3),
-        ...chartRes.json?.result.slice(0, 3),
-        ...ifQuery,
-      ],
-    );
-    return results;
-  });
+  return Promise.all(baseBatch).then(
+    // @ts-ignore
+    ([recentsRes, editedDash, editedChart, createdByDash, createdByChart]) => {
+      const res: any = {
+        editedDash: editedDash.json?.result.slice(0, 3),
+        editedChart: editedChart.json?.result.slice(0, 3),
+        createdByDash: createdByDash.json?.result.slice(0, 3),
+        createdByChart: createdByChart.json?.result.slice(0, 3),
+      };
+      if (recentsRes.json.length === 0) {
+        const newBatch = [
+          SupersetClient.get({ endpoint: `/api/v1/chart/?q=${getParams()}` }),
+          SupersetClient.get({
+            endpoint: `/api/v1/dashboard/?q=${getParams()}`,
+          }),
+        ];
+        // @ts-ignore
+        return Promise.all(newBatch)
+          .then(([chartRes, dashboardRes]) => {
+            res.examples = [
+              ...chartRes.json.result,
+              ...dashboardRes.json.result,
+            ];
+            return res;
+          })
+          .catch(e => console.log('err', e));
+      }
+      res.viewed = recentsRes.json;
+      return res;
+    },
+  );
 };
 
 export const createFetchRelated = createFetchResourceMethod('related');
@@ -86,3 +142,39 @@ export function createErrorHandler(handleErrorFunc: (errMsg?: string) => void) {
     handleErrorFunc(parsedError.message || parsedError.error);
   };
 }
+
+export function handleDashboardDelete(id: string) {
+  return SupersetClient.delete({
+    endpoint: `/api/v1/dashboard/${id}`,
+  });
+}
+
+const breakpoints = [576, 768, 992, 1200];
+export const mq = breakpoints.map(bp => `@media (max-width: ${bp}px)`);
+
+export const CardContainer = styled.div`
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(31%, max-content));
+  ${[mq[3]]} {
+    grid-template-columns: repeat(auto-fit, minmax(31%, max-content));
+  }
+
+  ${[mq[2]]} {
+    grid-template-columns: repeat(auto-fit, minmax(48%, max-content));
+  }
+
+  ${[mq[1]]} {
+    grid-template-columns: repeat(auto-fit, minmax(48%, max-content));
+  }
+  grid-gap: ${({ theme }) => theme.gridUnit * 8}px;
+  justify-content: left;
+  padding: ${({ theme }) => theme.gridUnit * 2}px
+    ${({ theme }) => theme.gridUnit * 6}px;
+`;
+
+export const IconContainer = styled.div`
+  svg {
+    vertical-align: -7px;
+    color: ${({ theme }) => theme.colors.primary.dark1};
+  }
+`;
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index 669838d..02d2cbc 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -17,11 +17,15 @@
  * under the License.
  */
 import React, { useEffect, useState } from 'react';
-import rison from 'rison';
-import moment from 'moment';
+import moment from 'antd/node_modules/moment';
+import { styled, t } from '@superset-ui/core';
+
 import ListViewCard from 'src/components/ListViewCard';
 import { addDangerToast } from 'src/messageToasts/actions';
-import { createBatchMethod, createErrorHandler } from '../utils';
+import SubMenu from 'src/components/Menu/SubMenu';
+import { reject } from 'lodash';
+import { getBatchData, createErrorHandler } from '../utils';
+import EmptyState from './EmptyState';
 
 interface MapProps {
   action?: string;
@@ -35,88 +39,129 @@ interface MapProps {
   label: string;
   id: string;
   table: object;
+  item_url: string;
 }
 
 interface ActivityProps {
   user: {
-    userId: string | number;
+    userId: string;
   };
-  activityFilter: string;
 }
 
-export default function ActivityTable({ user, activityFilter }: ActivityProps) {
-  const [active, setActiveState] = useState([]);
-  const [loading, setLoading] = useState(false);
-  // this API uses Log for data which in some cases is can be empty
-  // const recent = `/superset/recent_activity/${user.userId}/?limit=5`;
-  const filters = {
-    // Chart and dashbaord uses same filters
-    // for edited and created
-    edited: [
-      {
-        col: 'changed_by',
-        opr: 'rel_o_m',
-        value: `${user.userId}`,
-      },
-    ],
-    created: [
-      {
-        col: 'created_by',
-        opr: 'rel_o_m',
-        value: `${user.userId}`,
-      },
-    ],
-  };
+interface ActivityData {
+  Created: Array<object>;
+  Edited: Array<object>;
+  Viewed: Array<object>;
+  Examples: Array<object>;
+}
 
-  const setBatchData = (q: string, created?: string) => {
-    createBatchMethod(q, created)
-      .then((res: Array<object>) =>
-        // @ts-ignore
-        setActiveState(res),
-      )
-      .catch(() => addDangerToast('Oops something went wrong'));
-  };
+const ActivityContainer = styled.div`
+  margin-left: 10px;
+  margin-top: -15px;
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(31%, max-content));
+  grid-gap: ${({ theme }) => theme.gridUnit * 8}px;
+  justify-content: left;
+  padding: ${({ theme }) => theme.gridUnit * 2}px
+    ${({ theme }) => theme.gridUnit * 4}px;
+  .ant-card-meta-avatar {
+    margin-top: 5px;
+  }
+  .ant-card-meta-title {
+    font-weight: 700;
+  }
+`;
+
+export default function ActivityTable({ user }: ActivityProps) {
+  const [activityData, setActivityData] = useState<ActivityData | {}>({});
+  const [loading, setLoading] = useState(true);
+  const [activeChild, setActiveChild] = useState('Viewed');
+  // this api uses log for data which in some cases is can be empty
+  const recent = `/superset/recent_activity/${user.userId}/?limit=5`;
 
   const getFilterTitle = (e: MapProps) => {
     if (e.dashboard_title) return e.dashboard_title;
     if (e.label) return e.label;
     if (e.url && !e.table) return e.item_title;
+    if (e.item_title) return e.item_title;
     return e.slice_name;
   };
 
   const getIconName = (e: MapProps) => {
     if (e.sql) return 'sql';
-    if (e.url.indexOf('dashboard') !== -1) {
+    if (e.url?.indexOf('dashboard') !== -1) {
       return 'nav-dashboard';
     }
-    if (e.url.indexOf('explore') !== -1) {
+    if (e.url?.indexOf('explore') !== -1) {
+      return 'nav-charts';
+    }
+    if (e.item_url?.indexOf('explore') !== -1) {
       return 'nav-charts';
     }
     return '';
   };
 
-  const getData = () => {
-    const queryParams = rison.encode({
-      order_column: 'changed_on_delta_humanized',
-      order_direction: 'desc',
-      page: 0,
-      page_size: 0,
-      filters: activityFilter !== 'Created' ? filters.edited : filters.created,
+  const tabs = [
+    {
+      name: 'Edited',
+      label: t('Edited'),
+      onClick: () => {
+        setActiveChild('Edited');
+      },
+    },
+    {
+      name: 'Created',
+      label: t('Created'),
+      onClick: () => {
+        setActiveChild('Created');
+      },
+    },
+  ];
+
+  if (activityData.Viewed) {
+    tabs.unshift({
+      name: 'Viewed',
+      label: t('Viewed'),
+      onClick: () => {
+        setActiveChild('Viewed');
+      },
     });
-    if (activityFilter === 'Edited') {
-      setBatchData(queryParams);
-    }
-    if (activityFilter === 'Created') {
-      setBatchData(queryParams, 'createdBy');
-    }
-  };
+  } else {
+    tabs.unshift({
+      name: 'Examples',
+      label: t('Examples'),
+      onClick: () => {
+        setActiveChild('Examples');
+      },
+    });
+  }
 
   useEffect(() => {
-    getData();
-  }, [activityFilter]);
+    getBatchData(user.userId, recent)
+      .then(r => {
+        const data: any = {
+          Created: [...r.createdByChart, ...r.createdByDash],
+          Edited: [...r.editedChart, ...r.editedDash],
+        };
+        if (r.viewed) {
+          const filtered = reject(r.viewed, ['item_url', null]).map(r => r);
+          data.Viewed = filtered;
+          setActiveChild('Viewed');
+        } else {
+          data.Examples = r.examples;
+          setActiveChild('Examples');
+        }
+        setActivityData(data);
+        setLoading(false);
+      })
+      .catch(e => {
+        setLoading(false);
+        addDangerToast(`e ${e}`);
+      });
+  }, []);
 
   const renderActivity = () => {
-    return active.map((e: MapProps, i) => (
+    return activityData[activeChild].map((e: MapProps, i: any) => (
       <ListViewCard
         key={`${i}`}
         isRecent
@@ -125,12 +170,32 @@ export default function ActivityTable({ user, activityFilter }: ActivityProps) {
         imgFallbackURL=""
         url={e.sql ? `/supserset/sqllab?queryId=${e.id}` : e.url}
         title={getFilterTitle(e)}
-        description={moment.utc(e.changed_on_utc).fromNow()}
+        description={`Last Edited: ${moment(e.changed_on_utc).format(
+          'MM/DD/YYYY HH:mm:ss',
+        )}`}
         avatar={getIconName(e)}
         actions={null}
       />
     ));
   };
-
-  return <> {renderActivity()} </>;
+  if (loading) return <>loading ...</>;
+  return (
+    <>
+      <>
+        <SubMenu
+          activeChild={activeChild}
+          name=""
+          // eslint-disable-next-line react/no-children-prop
+          children={tabs}
+        />
+        <>
+          {activityData[activeChild]?.length > 0 ? (
+            <ActivityContainer>{renderActivity()}</ActivityContainer>
+          ) : (
+            <EmptyState tableName="RECENTS" tab="Mine" />
+          )}
+        </>
+      </>
+    </>
+  );
 }
diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
index d0ccaee..99d51c0 100644
--- a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
@@ -16,15 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React, { useEffect } from 'react';
+import React, { useEffect, useState } from 'react';
 import { t } from '@superset-ui/core';
 import { useListViewResource, useChartEditModal } from 'src/views/CRUD/hooks';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import PropertiesModal from 'src/explore/components/PropertiesModal';
 import { User } from 'src/types/bootstrapTypes';
-import Owner from 'src/types/Owner';
+import Icon from 'src/components/Icon';
 import ChartCard from 'src/views/CRUD/chart/ChartCard';
 import Chart from 'src/types/Chart';
+import SubMenu from 'src/components/Menu/SubMenu';
+import EmptyState from './EmptyState';
+import { CardContainer, IconContainer } from '../utils';
 
 const PAGE_SIZE = 3;
 
@@ -37,7 +40,6 @@ interface ChartTableProps {
 }
 
 function ChartTable({
-  chartFilter,
   user,
   addDangerToast,
   addSuccessToast,
@@ -57,6 +59,8 @@ function ChartTable({
     closeChartEditModal,
   } = useChartEditModal(setCharts, charts);
 
+  const [chartFilter, setChartFilter] = useState('Favorite');
+
   const getFilters = () => {
     const filters = [];
 
@@ -110,19 +114,64 @@ function ChartTable({
         />
       )}
 
-      {charts.map((e, i) => (
-        <ChartCard
-          key={`${i}`}
-          openChartEditModal={openChartEditModal}
-          loading={loading}
-          chart={e}
-          hasPerm={hasPerm}
-          bulkSelectEnabled={bulkSelectEnabled}
-          refreshData={refreshData}
-          addDangerToast={addDangerToast}
-          addSuccessToast={addSuccessToast}
-        />
-      ))}
+      <SubMenu
+        activeChild={chartFilter}
+        name=""
+        // eslint-disable-next-line react/no-children-prop
+        children={[
+          {
+            name: 'Favorite',
+            label: t('Favorite'),
+            onClick: () => setChartFilter('Favorite'),
+          },
+          {
+            name: 'Mine',
+            label: t('Mine'),
+            onClick: () => setChartFilter('Mine'),
+          },
+        ]}
+        buttons={[
+          {
+            name: (
+              <IconContainer>
+                <Icon name="plus-small" /> Chart{' '}
+              </IconContainer>
+            ),
+            buttonStyle: 'tertiary',
+            onClick: () => {
+              // @ts-ignore
+              window.location = '/chart/add';
+            },
+          },
+          {
+            name: 'View All »',
+            buttonStyle: 'link',
+            onClick: () => {
+              // @ts-ignore
+              window.location = '/chart/list';
+            },
+          },
+        ]}
+      />
+      {charts.length ? (
+        <CardContainer>
+          {charts.map((e, i) => (
+            <ChartCard
+              key={`${i}`}
+              openChartEditModal={openChartEditModal}
+              loading={loading}
+              chart={e}
+              hasPerm={hasPerm}
+              bulkSelectEnabled={bulkSelectEnabled}
+              refreshData={refreshData}
+              addDangerToast={addDangerToast}
+              addSuccessToast={addSuccessToast}
+            />
+          ))}
+        </CardContainer>
+      ) : (
+        <EmptyState tableName="CHARTS" tab={chartFilter} />
+      )}
     </>
   );
 }
diff --git a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
index c47b2dd..ae3daeb 100644
--- a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
@@ -16,30 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React, { useEffect } from 'react';
-import { t } from '@superset-ui/core';
-import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
+import React, { useEffect, useState } from 'react';
+import { SupersetClient, t } from '@superset-ui/core';
+import { useListViewResource } from 'src/views/CRUD/hooks';
+import { Dashboard, DashboardTableProps } from 'src/views/CRUD/types';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
-import Owner from 'src/types/Owner';
-import { DashboardTableProps } from 'src/views/CRUD/types';
+import PropertiesModal from 'src/dashboard/components/PropertiesModal';
 import DashboardCard from 'src/views/CRUD/dashboard/DashboardCard';
+import SubMenu from 'src/components/Menu/SubMenu';
+import Icon from 'src/components/Icon';
+import EmptyState from './EmptyState';
+import { createErrorHandler, CardContainer, IconContainer } from '../utils';
 
 const PAGE_SIZE = 3;
 
-interface Dashboard {
-  changed_by_name: string;
-  changed_by_url: string;
-  changed_on_delta_humanized: string;
-  changed_by: string;
-  dashboard_title: string;
-  id: number;
-  published: boolean;
-  url: string;
-  thumbnail_url: string;
-  owners: Owner[];
-  loading: boolean;
-}
-
 export interface FilterValue {
   col: string;
   operator: string;
@@ -47,14 +37,13 @@ export interface FilterValue {
 }
 
 function DashboardTable({
-  dashboardFilter,
   user,
   addDangerToast,
   addSuccessToast,
-  search,
 }: DashboardTableProps) {
   const {
     state: { loading, resourceCollection: dashboards, bulkSelectEnabled },
+    setResourceCollection: setDashboards,
     hasPerm,
     refreshData,
     fetchData,
@@ -64,6 +53,31 @@ function DashboardTable({
     addDangerToast,
   );
 
+  const [editModal, setEditModal] = useState<Dashboard | null>(null);
+  const [dashboardFilter, setDashboardFilter] = useState('Favorite');
+
+  const handleDashboardEdit = (edits: Dashboard) => {
+    return SupersetClient.get({
+      endpoint: `/api/v1/dashboard/${edits.id}`,
+    }).then(
+      ({ json = {} }) => {
+        setDashboards(
+          dashboards.map(dashboard => {
+            if (dashboard.id === json.id) {
+              return json.result;
+            }
+            return dashboard;
+          }),
+        );
+      },
+      createErrorHandler(errMsg =>
+        addDangerToast(
+          t('An error occurred while fetching dashboards: %s', errMsg),
+        ),
+      ),
+    );
+  };
+
   const getFilters = () => {
     const filters = [];
 
@@ -80,15 +94,16 @@ function DashboardTable({
         value: true,
       });
     }
-    filters.concat([
-      {
-        id: 'dashboard_title',
-        operator: 'ct',
-        value: search,
-      },
-    ]);
     return filters;
   };
+  const subMenus = [];
+  if (dashboards.length > 0 && dashboardFilter === 'favorite') {
+    subMenus.push({
+      name: 'Favorite',
+      label: t('Favorite'),
+      onClick: () => setDashboardFilter('Favorite'),
+    });
+  }
 
   useEffect(() => {
     fetchData({
@@ -106,18 +121,73 @@ function DashboardTable({
 
   return (
     <>
-      {dashboards.map(e => (
-        <DashboardCard
-          {...{
-            dashboard: e,
-            hasPerm,
-            bulkSelectEnabled,
-            refreshData,
-            addDangerToast,
-            addSuccessToast,
-          }}
+      <SubMenu
+        activeChild={dashboardFilter}
+        name=""
+        // eslint-disable-next-line react/no-children-prop
+        children={[
+          {
+            name: 'Favorite',
+            label: t('Favorite'),
+            onClick: () => setDashboardFilter('Favorite'),
+          },
+          {
+            name: 'Mine',
+            label: t('Mine'),
+            onClick: () => setDashboardFilter('Mine'),
+          },
+        ]}
+        buttons={[
+          {
+            name: (
+              <IconContainer>
+                <Icon name="plus-small" /> Dashboard{' '}
+              </IconContainer>
+            ),
+            buttonStyle: 'tertiary',
+            onClick: () => {
+              // @ts-ignore
+              window.location = '/dashboard/new';
+            },
+          },
+          {
+            name: 'View All »',
+            buttonStyle: 'link',
+            onClick: () => {
+              // @ts-ignore
+              window.location = '/dashboard/list/';
+            },
+          },
+        ]}
+      />
+      {editModal && (
+        <PropertiesModal
+          dashboardId={editModal?.id}
+          show
+          onHide={() => setEditModal(null)}
+          onSubmit={handleDashboardEdit}
         />
-      ))}
+      )}
+      {dashboards.length > 0 ? (
+        <CardContainer>
+          {dashboards.map(e => (
+            <DashboardCard
+              {...{
+                dashboard: e,
+                hasPerm,
+                bulkSelectEnabled,
+                refreshData,
+                addDangerToast,
+                addSuccessToast,
+                loading,
+                openDashboardEditModal: dashboard => setEditModal(dashboard),
+              }}
+            />
+          ))}
+        </CardContainer>
+      ) : (
+        <EmptyState tableName="DASHBOARDS" tab={dashboardFilter} />
+      )}
     </>
   );
 }
diff --git a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
new file mode 100644
index 0000000..e1c2936
--- /dev/null
+++ b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
@@ -0,0 +1,112 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import Button from 'src/components/Button';
+import { styled } from '@superset-ui/core';
+import Icon from 'src/components/Icon';
+import { IconContainer } from '../utils';
+
+interface EmptyStateProps {
+  tableName: string;
+  tab?: string;
+}
+
+const Container = styled.div`
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  img {
+    width: 87px;
+    display: block;
+    margin: 0 auto;
+  }
+  div:nth-child(2) {
+    text-align: center;
+    margin-top: 15px;
+    color: ${({ theme }) => theme.colors.grayscale.dark1};
+    font-weight: 400;
+  }
+  button {
+    margin: 0 auto;
+    padding: 6px 27px;
+    margin-top: 10px;
+    svg {
+      color: white;
+    }
+  }
+`;
+
+export default function EmptyState({ tableName, tab }: EmptyStateProps) {
+  const mineRedirects = {
+    DASHBOARDS: '/dashboard/new',
+    CHARTS: '/chart/add',
+    SAVED_QUERIES: '/superset/sqllab',
+  };
+  const favRedirects = {
+    DASHBOARDS: '/dashboard/list/',
+    CHARTS: '/chart/list',
+    SAVED_QUERIES: '/savedqueryview/list/',
+  };
+  const Mine = (
+    <div>
+      <div>{`No ${tableName.toLowerCase()} yet`}</div>
+      <Button
+        buttonStyle="primary"
+        onClick={() => {
+          window.location = mineRedirects[tableName];
+        }}
+      >
+        <IconContainer>
+          <Icon name="plus-small" /> {tableName}
+        </IconContainer>
+      </Button>
+    </div>
+  );
+  const span = (
+    <div>
+      Recently viewed charts, dashboards, and saved queries will appear here
+    </div>
+  );
+
+  if (tab === 'Mine') {
+    return (
+      <Container>
+        <img src="/static/assets/images/union.png" alt="union.png" />
+        {tableName === 'RECENTS' ? span : Mine}
+      </Container>
+    );
+  }
+
+  return (
+    <Container>
+      <img src="/static/assets/images/star-circle.png" alt="star.png" />
+      <div>
+        <div>You don't have any favorites yets!</div>
+        <Button
+          buttonStyle="primary"
+          onClick={() => {
+            window.location = favRedirects[tableName];
+          }}
+        >
+          SEE ALL {tableName}
+        </Button>
+      </div>
+    </Container>
+  );
+}
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
index c60d4fb..333d394 100644
--- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -17,18 +17,24 @@
  * under the License.
  */
 import React, { useEffect, useState } from 'react';
-import { t } from '@superset-ui/core';
+import { t, styled, SupersetClient } from '@superset-ui/core';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import { Dropdown, Menu } from 'src/common/components';
-import { useListViewResource } from 'src/views/CRUD/hooks';
+import { useListViewResource, copyQueryLink } from 'src/views/CRUD/hooks';
 import ListViewCard from 'src/components/ListViewCard';
+import DeleteModal from 'src/components/DeleteModal';
 import Icon from 'src/components/Icon';
-import { addDangerToast } from 'src/messageToasts/actions';
+import { SavedQueryObject } from 'src/views/CRUD/types';
+import SubMenu from 'src/components/Menu/SubMenu';
+import EmptyState from './EmptyState';
+
+import { IconContainer, CardContainer, createErrorHandler } from '../utils';
 
 const PAGE_SIZE = 3;
 
 interface Query {
-  sql_tables: array;
+  id: number;
+  sql_tables: Array<any>;
   database: {
     database_name: string;
   };
@@ -36,6 +42,7 @@ interface Query {
   description: string;
   end_time: string;
   addDangerToast: () => void;
+  label: string;
 }
 
 interface SavedQueriesProps {
@@ -45,11 +52,46 @@ interface SavedQueriesProps {
   queryFilter: string;
 }
 
-const SavedQueries = ({ user, queryFilter }: SavedQueriesProps) => {
+const NoData = styled.div`
+  .create-your-query {
+    display: block;
+    margin: 0 auto;
+  }
+`;
+
+const SavedQueries = ({
+  user,
+  addDangerToast,
+  addSuccessToast,
+}: SavedQueriesProps) => {
   const {
     state: { loading, resourceCollection: queries },
+    hasPerm,
     fetchData,
+    refreshData,
   } = useListViewResource<Query>('saved_query', t('query'), addDangerToast);
+  const [queryFilter, setQueryFilter] = useState('Favorite');
+  const [queryDeleteModal, setQueryDeleteModal] = useState(false);
+  const [currentlyEdited, setCurrentlyEdited] = useState(null);
+
+  const canEdit = hasPerm('can_edit');
+  const canDelete = hasPerm('can_delete');
+
+  const handleQueryDelete = ({ id, label }: SavedQueryObject) => {
+    SupersetClient.delete({
+      endpoint: `/api/v1/saved_query/${id}`,
+    }).then(
+      () => {
+        refreshData();
+        setQueryDeleteModal(false);
+        addSuccessToast(t('Deleted: %s', label));
+      },
+      createErrorHandler(errMsg =>
+        addDangerToast(t('There was an issue deleting %s: %s', label, errMsg)),
+      ),
+    );
+  };
+
   const getFilters = () => {
     const filters = [];
 
@@ -83,36 +125,117 @@ const SavedQueries = ({ user, queryFilter }: SavedQueriesProps) => {
     });
   }, [queryFilter]);
 
-  const menu = (
+  const renderMenu = (query: Query) => (
     <Menu>
-      <Menu.Item>Delete</Menu.Item>
+      {canEdit && (
+        <Menu.Item
+          onClick={() => {
+            // @ts-ignore
+            window.location = `/superset/sqllab?savedQueryId=${query.id}`;
+          }}
+        >
+          Edit
+        </Menu.Item>
+      )}
+      <Menu.Item
+        onClick={() => copyQueryLink(query.id, addDangerToast, addSuccessToast)}
+      >Share</Menu.Item>
+      {canDelete && (
+        <Menu.Item
+          onClick={() => {
+            setQueryDeleteModal(true);
+            setCurrentlyEdited(query);
+          }}
+        >
+          Delete
+        </Menu.Item>
+      )}
     </Menu>
   );
 
   return (
     <>
-      {queries ? (
-        queries.map(q => (
-          <ListViewCard
-            imgFallbackURL="/static/assets/images/dashboard-card-fallback.png"
-            imgURL=""
-            title={q.database.database_name}
-            rows={q.rows}
-            tableName={q.sql_tables[0].table}
-            loading={loading}
-            description={t('Last run ', q.end_time)}
-            showImg={false}
-            actions={
-              <ListViewCard.Actions>
-                <Dropdown overlay={menu}>
-                  <Icon name="more-horiz" />
-                </Dropdown>
-              </ListViewCard.Actions>
+      {queryDeleteModal && (
+        <DeleteModal
+          description={t(
+            'This action will permanently delete the saved query.',
+          )}
+          onConfirm={() => {
+            if (queryDeleteModal) {
+              handleQueryDelete(currentlyEdited);
             }
-          />
-        ))
+          }}
+          onHide={() => {
+            setQueryDeleteModal(false);
+          }}
+          open
+          title={t('Delete Query?')}
+        />
+      )}
+      <SubMenu
+        activeChild={queryFilter}
+        name=""
+        // eslint-disable-next-line react/no-children-prop
+        children={[
+          {
+            name: 'Favorite',
+            label: t('Favorite'),
+            onClick: () => setQueryFilter('Favorite'),
+          },
+          {
+            name: 'Mine',
+            label: t('Mine'),
+            onClick: () => setQueryFilter('Mine'),
+          },
+        ]}
+        buttons={[
+          {
+            name: (
+              <IconContainer>
+                <Icon name="plus-small" /> SQL Query{' '}
+              </IconContainer>
+            ),
+            buttonStyle: 'tertiary',
+            onClick: () => {
+              // @ts-ignore
+              window.location = '/superset/sqllab';
+            },
+          },
+          {
+            name: 'View All »',
+            buttonStyle: 'link',
+            onClick: () => {
+              // @ts-ignore
+              window.location = '/savedqueryview/list';
+            },
+          },
+        ]}
+      />
+      {queries.length > 0 ? (
+        <CardContainer>
+          {queries.map(q => (
+            <ListViewCard
+              imgFallbackURL=""
+              imgURL=""
+              title={q.label}
+              rows={q.rows}
+              tableName={q.sql_tables[0]?.table}
+              tables={q.sql_tables?.length}
+              loading={loading}
+              description={t('Last run ', q.end_time)}
+              showImg={false}
+              actions={
+                <ListViewCard.Actions>
+                  <Dropdown overlay={renderMenu(q)}>
+                    <Icon name="more-horiz" />
+                  </Dropdown>
+                </ListViewCard.Actions>
+              }
+            />
+          ))}
+        </CardContainer>
       ) : (
-        <span>You have no Saved Queries!</span>
+        <EmptyState tableName="SAVED_QUERIES" tab={queryFilter} />
       )}
     </>
   );
diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index f0934e1..7f7464b 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -16,8 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React, { useState } from 'react';
-import SubMenu from 'src/components/Menu/SubMenu';
+import React from 'react';
 import { styled, t } from '@superset-ui/core';
 import { Collapse } from 'src/common/components';
 import { User } from 'src/types/bootstrapTypes';
@@ -35,213 +34,57 @@ interface WelcomeProps {
 }
 
 const WelcomeContainer = styled.div`
+  background-color: ${({ theme }) => theme.colors.grayscale.light4};
   nav {
+    margin-top: -15px;
     background-color: ${({ theme }) => theme.colors.grayscale.light4};
     &:after {
       content: '';
       display: block;
       border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
       margin: 0px 26px;
+      position: relative;
+      top: -13px;
+    }
+    .nav.navbar-nav {
+      & > li:nth-child(1),
+      & > li:nth-child(2),
+      & > li:nth-child(3) {
+        margin-top: 8px;
+      }
+    }
+    button {
+      padding: 3px 21px;
+    }
+    .navbar-right {
+      position: relative;
+      top: 11px;
     }
   }
   .ant-card.ant-card-bordered {
     border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
   }
-`;
-
-const ActivityContainer = styled.div`
-  display: grid;
-  grid-template-columns: repeat(auto-fit, minmax(31%, max-content));
-  grid-gap: ${({ theme }) => theme.gridUnit * 8}px;
-  justify-content: center;
-  padding: ${({ theme }) => theme.gridUnit * 2}px
-    ${({ theme }) => theme.gridUnit * 4}px;
-`;
-
-const IconContainer = styled.div`
-  svg {
-    vertical-align: -7px;
-    color: ${({ theme }) => theme.colors.primary.dark1};
+  .ant-collapse-header {
+    font-weight: 500;
+    font-size: 16px;
   }
 `;
-export const CardContainer = styled.div`
-  display: grid;
-  grid-template-columns: repeat(auto-fit, minmax(459px, 1fr));
-  grid-gap: ${({ theme }) => theme.gridUnit * 8}px;
-  justify-content: left;
-  padding: ${({ theme }) => theme.gridUnit * 2}px
-    ${({ theme }) => theme.gridUnit * 6}px;
-`;
 
 export default function Welcome({ user }: WelcomeProps) {
-  const [queryFilter, setQueryFilter] = useState('Favorite');
-  const [activityFilter, setActivityFilter] = useState('Edited');
-  const [dashboardFilter, setDashboardFilter] = useState('Favorite');
-  const [chartFilter, setChartFilter] = useState('Favorite');
-
-  function ExpandIcon(): React.ReactNode {
-    return <Icon name="caret-right" />;
-  }
-
   return (
     <WelcomeContainer>
       <Collapse defaultActiveKey={['1']} ghost>
         <Panel header={t('Recents')} key="1">
-          <SubMenu
-            activeChild={activityFilter}
-            name=""
-            // eslint-disable-next-line react/no-children-prop
-            children={[
-              {
-                name: 'Edited',
-                label: t('Edited'),
-                onClick: () => setActivityFilter('Edited'),
-              },
-              {
-                name: 'Created',
-                label: t('Created'),
-                onClick: () => setActivityFilter('Created'),
-              },
-            ]}
-          />
-          <ActivityContainer>
-            <ActivityTable user={user} activityFilter={activityFilter} />
-          </ActivityContainer>
+          <ActivityTable user={user} />
         </Panel>
-
-        <Panel header={t('Dashboards')} key="2">
-          <SubMenu
-            activeChild={dashboardFilter}
-            name=""
-            // eslint-disable-next-line react/no-children-prop
-            children={[
-              {
-                name: 'Favorite',
-                label: t('Favorite'),
-                onClick: () => setDashboardFilter('Favorite'),
-              },
-              {
-                name: 'Mine',
-                label: t('Mine'),
-                onClick: () => setDashboardFilter('Mine'),
-              },
-            ]}
-            buttons={[
-              {
-                name: (
-                  <IconContainer>
-                    <Icon name="plus-small" /> Dashboard{' '}
-                  </IconContainer>
-                ),
-                buttonStyle: 'tertiary',
-                onClick: () => {
-                  // @ts-ignore
-                  window.location = '/dashboard/new';
-                },
-              },
-              {
-                name: 'View All »',
-                buttonStyle: 'link',
-                onClick: () => {
-                  // @ts-ignore
-                  window.location = '/dashboard/list/';
-                },
-              },
-            ]}
-          />
-          <CardContainer>
-            <DashboardTable dashboardFilter={dashboardFilter} user={user} />
-          </CardContainer>
+        <Panel header={t('Dashboards')} key="1">
+          <DashboardTable user={user} />
         </Panel>
-
-        <Panel header={t('Saved Queries')} key="3">
-          <SubMenu
-            activeChild={queryFilter}
-            name=""
-            // eslint-disable-next-line react/no-children-prop
-            children={[
-              {
-                name: 'Favorite',
-                label: t('Favorite'),
-                onClick: () => setQueryFilter('Favorite'),
-              },
-              {
-                name: 'Mine',
-                label: t('Mine'),
-                onClick: () => setQueryFilter('Mine'),
-              },
-            ]}
-            buttons={[
-              {
-                name: (
-                  <IconContainer>
-                    <Icon name="plus-small" /> SQL Query{' '}
-                  </IconContainer>
-                ),
-                buttonStyle: 'tertiary',
-                onClick: () => {
-                  // @ts-ignore
-                  window.location = '/superset/sqllab';
-                },
-              },
-              {
-                name: 'View All »',
-                buttonStyle: 'link',
-                onClick: () => {
-                  // @ts-ignore
-                  window.location = '/savedqueryview/list';
-                },
-              },
-            ]}
-          />
-          <CardContainer>
-            <SavedQueries user={user} queryFilter={queryFilter} />
-          </CardContainer>
+        <Panel header={t('Saved Queries')} key="1">
+          <SavedQueries user={user} />
         </Panel>
-        <Panel header={t('Charts')} key="4">
-          <SubMenu
-            activeChild={chartFilter}
-            name=""
-            // eslint-disable-next-line react/no-children-prop
-            children={[
-              {
-                name: 'Favorite',
-                label: t('Favorite'),
-                onClick: () => setChartFilter('Favorite'),
-              },
-              {
-                name: 'Mine',
-                label: t('Mine'),
-                onClick: () => setChartFilter('Mine'),
-              },
-            ]}
-            buttons={[
-              {
-                name: (
-                  <IconContainer>
-                    <Icon name="plus-small" /> Chart{' '}
-                  </IconContainer>
-                ),
-                buttonStyle: 'tertiary',
-                onClick: () => {
-                  // @ts-ignore
-                  window.location = '/chart/add';
-                },
-              },
-              {
-                name: 'View All »',
-                buttonStyle: 'link',
-                onClick: () => {
-                  // @ts-ignore
-                  window.location = '/chart/list';
-                },
-              },
-            ]}
-          />
-
-          <CardContainer>
-            <ChartTable chartFilter={chartFilter} user={user} />
-          </CardContainer>
+        <Panel header={t('Charts')} key="1">
+          <ChartTable user={user} />
         </Panel>
       </Collapse>
     </WelcomeContainer>


[incubator-superset] 10/33: more updates

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 97c21c9cdc7a86f047e13f2cd2ca6280cdb59bbf
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Tue Oct 6 15:28:42 2020 -0700

    more updates
---
 .../views/CRUD/welcome/SavedQueries_spec.tsx       |   0
 .../views/CRUD/welcome/Welcome_spec.tsx            |   7 +-
 .../src/components/ListViewCard/index.tsx          |   8 +-
 superset-frontend/src/components/Menu/SubMenu.tsx  |  14 +-
 .../src/views/CRUD/chart/ChartCard.tsx             | 162 +++++++++++++++++++++
 .../src/views/CRUD/chart/ChartList.tsx             |   5 +-
 .../src/views/CRUD/dashboard/DashboardCard.tsx     |  31 +---
 superset-frontend/src/views/CRUD/types.ts          |  36 +++++
 .../src/views/CRUD/welcome/ActivityTable.tsx       |  63 ++++----
 .../src/views/CRUD/welcome/ChartTable.tsx          |  55 +++----
 .../src/views/CRUD/welcome/SavedQueries.tsx        | 129 +++++++++-------
 .../src/views/CRUD/welcome/Welcome.tsx             |  31 ++--
 superset/queries/saved_queries/api.py              |   2 +-
 13 files changed, 355 insertions(+), 188 deletions(-)

diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
index bf23ef1..5db15b9 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
@@ -17,7 +17,6 @@
  * under the License.
  */
 import React from 'react';
-import { Panel, Row, Tab } from 'react-bootstrap';
 import { shallow } from 'enzyme';
 
 import Welcome from 'src/views/CRUD/welcome/Welcome';
@@ -37,10 +36,8 @@ describe('Welcome', () => {
   it('is valid', () => {
     expect(React.isValidElement(<Welcome {...mockedProps} />)).toBe(true);
   });
-  it('renders 3 Tab, Panel, and Row components', () => {
+  it('renders 3 submenu components', () => {
     const wrapper = shallow(<Welcome {...mockedProps} />);
-    expect(wrapper.find(Tab)).toHaveLength(3);
-    expect(wrapper.find(Panel)).toHaveLength(3);
-    expect(wrapper.find(Row)).toHaveLength(3);
+    expect(wrapper.find('SubMenu')).toHaveLength(4);
   });
 });
diff --git a/superset-frontend/src/components/ListViewCard/index.tsx b/superset-frontend/src/components/ListViewCard/index.tsx
index eb2bbab..7fb69c0 100644
--- a/superset-frontend/src/components/ListViewCard/index.tsx
+++ b/superset-frontend/src/components/ListViewCard/index.tsx
@@ -151,10 +151,11 @@ interface CardProps {
   titleRight?: React.ReactNode;
   coverLeft?: React.ReactNode;
   coverRight?: React.ReactNode;
-  actions: React.ReactNode;
+  actions: React.ReactNode | null;
   showImg?: boolean;
   rows?: number | string;
   avatar?: string;
+  isRecent?: boolean;
 }
 
 function ListViewCard({
@@ -167,7 +168,7 @@ function ListViewCard({
   coverLeft,
   coverRight,
   actions,
-  avatar = 'nav-dashboard',
+  avatar,
   loading,
   imgPosition = 'top',
   showImg = true,
@@ -255,7 +256,8 @@ function ListViewCard({
             </>
           }
           description={description}
-          avatar={<IconComponent />}
+          // @ts-ignore
+          avatar={avatar ? <Icon name={avatar} /> : null}
         />
       )}
     </StyledCard>
diff --git a/superset-frontend/src/components/Menu/SubMenu.tsx b/superset-frontend/src/components/Menu/SubMenu.tsx
index 93df81c..0144a20 100644
--- a/superset-frontend/src/components/Menu/SubMenu.tsx
+++ b/superset-frontend/src/components/Menu/SubMenu.tsx
@@ -53,12 +53,24 @@ const StyledHeader = styled.header`
     li.active > a,
     li.active > div,
     li > a:hover,
+    li > a:focus,
     li > div:hover {
-      background-color: ${({ theme }) => theme.colors.secondary.light4};
+      background: ${({ theme }) => theme.colors.secondary.light4};
       border-bottom: none;
       border-radius: 4px;
     }
   }
+  .navbar-inverse {
+    .navbar-nav {
+      & > .active > a {
+        background: ${({ theme }) => theme.colors.secondary.light4};
+        &:hover,
+        &:focus {
+          background: ${({ theme }) => theme.colors.secondary.light4};
+        }
+      }
+    }
+  }
 `;
 
 type MenuClickHandler = React.MouseEventHandler<MenuItem>;
diff --git a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
new file mode 100644
index 0000000..a8a123c
--- /dev/null
+++ b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
@@ -0,0 +1,162 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import { useFavoriteStatus } from 'src/views/CRUD/hooks';
+// import { createFetchRelated, createErrorHandler } from 'src/views/CRUD/utils';
+import { SupersetClient, t } from '@superset-ui/core';
+import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
+import AvatarIcon from 'src/components/AvatarIcon';
+import Icon from 'src/components/Icon';
+import Chart from 'src/types/Chart';
+import ListViewCard from 'src/components/ListViewCard';
+import Label from 'src/components/Label';
+import { Dropdown, Menu } from 'src/common/components';
+import FaveStar from 'src/components/FaveStar';
+
+const FAVESTAR_BASE_URL = '/superset/favstar/slice';
+
+interface ChartCardProps {
+  chart: Chart;
+  hasPerm: (perm: string) => boolean;
+  openChartEditModal: (chart: Chart) => void;
+  bulkSelectEnabled: boolean;
+  addDangerToast: (msg: string) => void;
+  addSuccessToast: (msg: string) => void;
+  refreshData: () => void;
+  loading: boolean;
+}
+
+export default function ChartCard({
+  chart,
+  hasPerm,
+  openChartEditModal,
+  bulkSelectEnabled,
+  addDangerToast,
+  addSuccessToast,
+  refreshData,
+  loading,
+}: ChartCardProps) {
+  const canEdit = hasPerm('can_edit');
+  const canDelete = hasPerm('can_delete');
+  const [favoriteStatusRef, fetchFaveStar, saveFaveStar] = useFavoriteStatus(
+    {},
+    FAVESTAR_BASE_URL,
+    addDangerToast,
+  );
+
+  function openChartEditModal(chart: Chart) {
+    setSliceCurrentlyEditing({
+      slice_id: chart.id,
+      slice_name: chart.slice_name,
+      description: chart.description,
+      cache_timeout: chart.cache_timeout,
+    });
+  }
+
+  function handleChartDelete({ id, slice_name: sliceName }: Chart) {
+    SupersetClient.delete({
+      endpoint: `/api/v1/chart/${id}`,
+    }).then(
+      () => {
+        refreshData();
+        addSuccessToast(t('Deleted: %s', sliceName));
+      },
+      () => {
+        addDangerToast(t('There was an issue deleting: %s', sliceName));
+      },
+    );
+  }
+
+  const menu = (
+    <Menu>
+      {canDelete && (
+        <Menu.Item>
+          <ConfirmStatusChange
+            title={t('Please Confirm')}
+            description={
+              <>
+                {t('Are you sure you want to delete')} <b>{chart.slice_name}</b>
+                ?
+              </>
+            }
+            onConfirm={() => handleChartDelete(chart)}
+          >
+            {confirmDelete => (
+              <div
+                role="button"
+                tabIndex={0}
+                className="action-button"
+                onClick={confirmDelete}
+              >
+                <ListViewCard.MenuIcon name="trash" /> Delete
+              </div>
+            )}
+          </ConfirmStatusChange>
+        </Menu.Item>
+      )}
+      {canEdit && (
+        <Menu.Item
+          role="button"
+          tabIndex={0}
+          onClick={() => openChartEditModal(chart)}
+        >
+          <ListViewCard.MenuIcon name="edit-alt" /> Edit
+        </Menu.Item>
+      )}
+    </Menu>
+  );
+  return (
+    <ListViewCard
+      loading={loading}
+      title={chart.slice_name}
+      url={bulkSelectEnabled ? undefined : chart.url}
+      imgURL={chart.thumbnail_url ?? ''}
+      imgFallbackURL="/static/assets/images/chart-card-fallback.png"
+      description={t('Last modified %s', chart.changed_on_delta_humanized)}
+      coverLeft={(chart.owners || []).slice(0, 5).map(owner => (
+        <AvatarIcon
+          key={owner.id}
+          uniqueKey={`${owner.username}-${chart.id}`}
+          firstName={owner.first_name}
+          lastName={owner.last_name}
+          iconSize={24}
+          textSize={9}
+        />
+      ))}
+      coverRight={
+        <Label bsStyle="secondary">{chart.datasource_name_text}</Label>
+      }
+      actions={
+        <ListViewCard.Actions>
+          <FaveStar
+            itemId={chart.id}
+            fetchFaveStar={fetchFaveStar}
+            saveFaveStar={saveFaveStar}
+            isStarred={!!favoriteStatusRef.current[chart.id]}
+            height={20}
+            width={20}
+          />
+          <Dropdown overlay={menu}>
+            <Icon name="more-horiz" />
+          </Dropdown>
+        </ListViewCard.Actions>
+      }
+    />
+  );
+}
diff --git a/superset-frontend/src/views/CRUD/chart/ChartList.tsx b/superset-frontend/src/views/CRUD/chart/ChartList.tsx
index 0586267..d326db7 100644
--- a/superset-frontend/src/views/CRUD/chart/ChartList.tsx
+++ b/superset-frontend/src/views/CRUD/chart/ChartList.tsx
@@ -430,7 +430,10 @@ function ChartList(props: ChartListProps) {
         hasPerm={hasPerm}
         openChartEditModal={openChartEditModal}
         bulkSelectEnabled={bulkSelectEnabled}
-        renderFaveStar={renderFaveStar}
+        addDangerToast={props.addDangerToast}
+        addSuccessToast={addSuccessToast}
+        refreshData={refreshData}
+        loading={loading}
       />
     );
   }
diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
index f42de20..e2e3b1f 100644
--- a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
+++ b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
@@ -8,39 +8,12 @@ import Icon from 'src/components/Icon';
 import Label from 'src/components/Label';
 import FacePile from 'src/components/FacePile';
 import FaveStar from 'src/components/FaveStar';
-import Owner from 'src/types/Owner';
-
+import { DashboardCardProps, Dashboard } from 'src/views/CRUD/types';
 import { createErrorHandler } from 'src/views/CRUD/utils';
 import { useFavoriteStatus } from 'src/views/CRUD/hooks';
 
 const FAVESTAR_BASE_URL = '/superset/favstar/Dashboard';
 
-interface Dashboard {
-  changed_by_name: string;
-  changed_by_url: string;
-  changed_on_delta_humanized: string;
-  changed_by: string;
-  dashboard_title: string;
-  slice_name?: string;
-  id: number;
-  published: boolean;
-  url: string;
-  thumbnail_url: string;
-  owners: Owner[];
-  loading: boolean;
-}
-
-interface DashboardCardProps {
-  isChart?: boolean;
-  dashboard: Dashboard;
-  hasPerm: (name: string) => boolean;
-  bulkSelectEnabled: boolean;
-  refreshData: () => void;
-  addDangerToast: (msg: string) => void;
-  addSuccessToast: (msg: string) => void;
-  openDashboardEditModal?: (d: Dashboard) => void;
-}
-
 function DashboardCard({
   isChart,
   dashboard,
@@ -132,7 +105,7 @@ function DashboardCard({
             openDashboardEditModal && openDashboardEditModal(dashboard)
           }
         >
-          <ListViewCard.MenuIcon name="pencil" /> Edit
+          <ListViewCard.MenuIcon name="edit-alt" /> Edit
         </Menu.Item>
       )}
     </Menu>
diff --git a/superset-frontend/src/views/CRUD/types.ts b/superset-frontend/src/views/CRUD/types.ts
index 91d88a3..7f5fce5 100644
--- a/superset-frontend/src/views/CRUD/types.ts
+++ b/superset-frontend/src/views/CRUD/types.ts
@@ -16,7 +16,43 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import { User } from 'src/types/bootstrapTypes';
+import Owner from 'src/types/Owner';
 
 export type FavoriteStatus = {
   [id: number]: boolean;
 };
+
+export interface DashboardTableProps {
+  addDangerToast: (message: string) => void;
+  addSuccessToast: (message: string) => void;
+  search: string;
+  dashboardFilter?: string;
+  user?: User;
+}
+
+interface Dashboard {
+  changed_by_name: string;
+  changed_by_url: string;
+  changed_on_delta_humanized: string;
+  changed_by: string;
+  dashboard_title: string;
+  slice_name?: string;
+  id: number;
+  published: boolean;
+  url: string;
+  thumbnail_url: string;
+  owners: Owner[];
+  loading: boolean;
+}
+
+export interface DashboardCardProps {
+  isChart?: boolean;
+  dashboard: Dashboard;
+  hasPerm: (name: string) => boolean;
+  bulkSelectEnabled: boolean;
+  refreshData: () => void;
+  addDangerToast: (msg: string) => void;
+  addSuccessToast: (msg: string) => void;
+  openDashboardEditModal?: (d: Dashboard) => void;
+}
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index ae496f2..b4c941c 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -17,34 +17,29 @@
  * under the License.
  */
 import React, { useEffect, useState } from 'react';
-import { styled, SupersetClient, t } from '@superset-ui/core';
-import Icon from 'src/components/Icon';
+import { SupersetClient, t } from '@superset-ui/core';
 import rison from 'rison';
 import moment from 'moment';
 import ListViewCard from 'src/components/ListViewCard';
 import { addDangerToast } from 'src/messageToasts/actions';
-import { MenuItem } from 'react-bootstrap';
 import { createBatchMethod, createErrorHandler } from '../utils';
 
 interface MapProps {
-  action: string;
-  item_title: string;
+  action?: string;
+  item_title?: string;
   slice_name: string;
   time: string;
   changed_on_utc: string;
+  item_url: string;
+}
+
+interface ActivityProps {
   user: {
     userId: string | number;
   };
   activityFilter: string;
-  item_url: string;
 }
 
-const Cards = styled.div`
-  width: 200px;
-  padding: 15px;
-  border: 1px solid #f0f0f0;
-`;
-
 const filters = {
   // Chart and dashbaord uses same filters
   // for edited and created
@@ -64,33 +59,34 @@ const filters = {
   ],
 };
 
-export default function ActivityTable({ user, activityFilter }: MapProps) {
+export default function ActivityTable({ user, activityFilter }: ActivityProps) {
   const [active, setActiveState] = useState([]);
+  const [loading, setLoading] = useState(false);
   const recent = `/superset/recent_activity/${user.userId}/?limit=10`;
   const setData = (endpoint: string) => {
-    SupersetClient.get(endpoint)
+    setLoading(true);
+    SupersetClient.get({ endpoint })
       .then(({ json }) => {
+        setLoading(false);
         setActiveState(json);
       })
-      .catch(e => {
-        createErrorHandler(err =>
+      .catch(() => {
+        setLoading(false);
+        createErrorHandler(() =>
           addDangerToast(t('There was an issue fetching your resource')),
         );
       });
   };
 
   const setBatchData = (q: string) => {
-    createBatchMethod(q).then((res: Array<object>) => {
-      setActiveState(res);
-    });
+    createBatchMethod(q).then((res: Array<object>) => setActiveState(res));
   };
 
-  const getIconName = (name: string): string => {
-    console.log('name', name)
+  const getIconName = (name: string | undefined) => {
     if (name === 'explore_json') return 'sql';
     if (name === 'dashboard') return 'nav-dashboard';
-    if (name === 'log') return 'nav-chart';
-    return 'sql';
+    if (name === 'log') return 'nav-charts';
+    return '';
   };
 
   const getData = () => {
@@ -102,7 +98,7 @@ export default function ActivityTable({ user, activityFilter }: MapProps) {
       filters: activityFilter !== 'Created' ? filters.edited : filters.created,
     });
     if (activityFilter === 'Viewed') {
-      setData({ endpoint: recent });
+      setData(recent);
     }
     if (activityFilter === 'Edited') {
       setBatchData(queryParams);
@@ -117,31 +113,22 @@ export default function ActivityTable({ user, activityFilter }: MapProps) {
   }, [activityFilter]);
 
   const renderActivity = () => {
-    console.log('e', active)
     return active.map((e: MapProps) => (
       <ListViewCard
-        isRecent={true}
+        isRecent
+        loading={loading}
+        imgURL={null}
+        imgFallbackURL={null}
         url={e.item_url}
         title={activityFilter === 'Viewed' ? e.item_title : e.slice_name}
         description={moment
           .utc(activityFilter === 'Viewd' ? e.time : e.changed_on_utc)
           .fromNow()}
         avatar={getIconName(e.action)}
+        actions={null}
       />
     ));
   };
 
   return <> {renderActivity()} </>;
 }
-/*
-      <a href={e.item_url}>
-        <Cards>
-          <div>{activityFilter === 'Viewed' ? e.item_title : e.slice_name}</div>
-          <div>
-            {moment
-              .utc(activityFilter === 'Viewd' ? e.time : e.changed_on_utc)
-              .fromNow()}
-          </div>
-        </Cards>
-      </>
-      */
\ No newline at end of file
diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
index 1958f88..e1223cb 100644
--- a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
@@ -18,18 +18,13 @@
  */
 import React, { useEffect } from 'react';
 import { t } from '@superset-ui/core';
-import { debounce } from 'lodash';
-import ListView, { FetchDataConfig } from 'src/components/ListView';
 import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import { User } from 'src/types/bootstrapTypes';
-import ListViewCard from 'src/components/ListViewCard';
-import { Dropdown, Menu } from 'src/common/components';
-import Icon from 'src/components/Icon';
-import Label from 'src/components/Label';
 import Owner from 'src/types/Owner';
-import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
-import DashboardCard from '../dashboard/DashboardCard';
+import ChartCard from 'src/views/CRUD/chart/ChartCard';
+import Chart from 'src/types/Chart';
+
 const PAGE_SIZE = 3;
 
 interface ChartTableProps {
@@ -61,16 +56,6 @@ interface ChartTableState {
   loading: boolean;
 }
 
-export interface FilterValue {
-  col: string;
-  operator: string;
-  value: string | boolean | number | null | undefined;
-}
-
-export interface FetchDataConfig {
-  filters: FilterValue[];
-}
-
 function ChartTable({
   chartFilter,
   user,
@@ -82,15 +67,14 @@ function ChartTable({
     hasPerm,
     refreshData,
     fetchData,
-  } = useListViewResource<Dashboard>('chart', t('chart'), addDangerToast);
-  console.log('dashboardFilter', chartFilter);
+  } = useListViewResource<Chart>('chart', t('chart'), addDangerToast);
   const getFilters = () => {
     const filters = [];
 
     if (chartFilter === 'Mine') {
       filters.push({
-        id: 'owners',
-        operator: 'rel_m_m',
+        id: 'created_by',
+        operator: 'rel_o_m',
         value: `${user?.userId}`,
       });
     } else {
@@ -98,10 +82,6 @@ function ChartTable({
         id: 'id',
         operator: 'chart_is_fav',
         value: true,
-       /* id: 'favorite', // API currently can't filter by favorite
-        operator: 'eq',
-        value: true,
-        */
       });
     }
     // Do we need search?
@@ -129,20 +109,19 @@ function ChartTable({
       filters: getFilters(),
     });
   }, [chartFilter]);
-  console.log("----charts: ", charts);
+
   return (
     <>
-      {charts.map(e => (
-        <DashboardCard
-          {...{
-            dashboard: e,
-            hasPerm,
-            bulkSelectEnabled,
-            refreshData,
-            addDangerToast,
-            addSuccessToast,
-          }}
-          isChart
+      {charts.map((e, i) => (
+        <ChartCard
+          key={`${i}`}
+          loading={loading}
+          chart={e}
+          hasPerm={hasPerm}
+          bulkSelectEnabled={bulkSelectEnabled}
+          refreshData={refreshData}
+          addDangerToast={addDangerToast}
+          addSuccessToast={addSuccessToast}
         />
       ))}
     </>
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
index cab929e..73c7547 100644
--- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -16,70 +16,97 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React from 'react';
+import React, { useEffect, useState } from 'react';
 import { t, SupersetClient } from '@superset-ui/core';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import { Dropdown, Menu } from 'src/common/components';
+import { useListViewResource } from 'src/views/CRUD/hooks';
 import ListViewCard from 'src/components/ListViewCard';
-// import FaveStar from 'src/components/FaveStar';
 import Icon from 'src/components/Icon';
+import { addDangerToast } from 'src/messageToasts/actions';
 
-interface StateProps {
-  queries: Array<object>;
+const PAGE_SIZE = 3;
+
+interface Query {
+  database: {
+    database_name: string;
+  };
+  rows: string;
+  description: string;
+  end_time: string;
+  addDangerToast: () => void;
 }
 
-class SavedQueries extends React.PureComponent {
-  constructor(props: StateProps) {
-    super(props);
-    this.state = {
-      queries: [],
-    };
-  }
+interface StateProps {
+  queries: Array<Query>;
+}
 
-  componentDidMount() {
-    this.fetchData();
-  }
+const SavedQueries = ({ user, queryFilter }) => {
+  const {
+    state: { loading, resourceCollection: queries },
+    fetchData,
+  } = useListViewResource<Query>('query', t('query'), addDangerToast);
+  const getFilters = () => {
+    const filters = [];
 
-  // eslint-disable-next-line consistent-return
-  fetchData = async () => {
-    try {
-      const { json } = await SupersetClient.get({
-        endpoint: `/api/v1/query/`,
+    if (queryFilter === 'Mine') {
+      filters.push({
+        id: 'created_by',
+        operator: 'rel_o_m',
+        value: `${user?.userId}`,
+      });
+    } else {
+      filters.push({
+        id: 'id',
+        operator: 'saved_query_is_fav',
+        value: true,
       });
-      this.setState({ queries: json.result });
-    } catch (e) {
-      return console.log(e);
     }
+    return filters;
   };
 
-  render() {
-    const menu = (
-      <Menu>
-        <Menu.Item>Delete</Menu.Item>
-      </Menu>
-    );
-    // console.log('queries', this.state.queries)
-    return (
-      <>
-        {this.state.queries.map(q => (
-          <ListViewCard
-            title={q.database.database_name}
-            rows={q.rows}
-            loading={false}
-            description={t('Last run ', q.end_time)}
-            showImg={false}
-            actions={
-              <ListViewCard.Actions>
-                <Dropdown overlay={menu}>
-                  <Icon name="more-horiz" />
-                </Dropdown>
-              </ListViewCard.Actions>
-            }
-          />
-        ))}
-      </>
-    );
-  }
-}
+  useEffect(() => {
+    fetchData({
+      pageIndex: 0,
+      pageSize: PAGE_SIZE,
+      sortBy: [
+        {
+          id: 'changed_on_delta_humanized',
+          desc: true,
+        },
+      ],
+      filters: getFilters(),
+    });
+  }, [queryFilter]);
+
+  const menu = (
+    <Menu>
+      <Menu.Item>Delete</Menu.Item>
+    </Menu>
+  );
+
+  return (
+    <>
+      {queries.map(q => (
+        <ListViewCard
+          imgFallbackURL={null}
+          imgURL={null}
+          title={q.database.database_name}
+          rows={q.rows}
+          loading={loading}
+          description={t('Last run ', q.end_time)}
+          showImg={false}
+          actions={
+            <ListViewCard.Actions>
+              <Dropdown overlay={menu}>
+                <Icon name="more-horiz" />
+              </Dropdown>
+            </ListViewCard.Actions>
+          }
+        />
+      ))}
+    </>
+  );
+};
 
 export default withToasts(SavedQueries);
diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index f4f1e7d..b4326fc 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -24,11 +24,11 @@ import { Collapse } from 'src/common/components';
 import { useQueryParam, StringParam, QueryParamConfig } from 'use-query-params';
 import { User } from 'src/types/bootstrapTypes';
 import Icon from 'src/components/Icon';
+
 import ActivityTable from './ActivityTable';
 import ChartTable from './ChartTable';
 import SavedQueries from './SavedQueries';
 import DashboardTable from './DashboardTable';
-import Icon from 'src/components/Icon';
 
 const { Panel } = Collapse;
 
@@ -79,11 +79,6 @@ function useSyncQueryState(
 }
 
 export default function Welcome({ user }: WelcomeProps) {
-  const [activeTab, setActiveTab] = useSyncQueryState(
-    'activeTab',
-    StringParam,
-    'all',
-  );
   const [queryFilter, setQueryFilter] = useState('Favorite');
   const [activityFilter, setActivityFilter] = useState('Viewed');
   const [dashboardFilter, setDashboardFilter] = useState('Favorite');
@@ -93,21 +88,13 @@ export default function Welcome({ user }: WelcomeProps) {
     StringParam,
     '',
   );
-  console.log('user', user);
-
 
   function ExpandIcon(): React.ReactNode {
-    return (
-      <Icon name="caret-right" />
-    )
+    return <Icon name="caret-right" />;
   }
 
   return (
-    <Collapse
-      defaultActiveKey={['1']}
-      expandIcon={ExpandIcon}
-      ghost
-    >
+    <Collapse defaultActiveKey={['1']} expandIcon={ExpandIcon} ghost>
       <Panel header={t('Recents')} key="1">
         <SubMenu
           activeChild={activityFilter}
@@ -130,10 +117,6 @@ export default function Welcome({ user }: WelcomeProps) {
               onClick: () => setActivityFilter('Created'),
             },
           ]}
-          links={{
-            link: `/chart/list`,
-            linkTitle: 'View All',
-          }}
         />
         <ActivityContainer>
           <ActivityTable user={user} activityFilter={activityFilter} />
@@ -166,6 +149,7 @@ export default function Welcome({ user }: WelcomeProps) {
               ),
               buttonStyle: 'tertiary',
               onClick: () => {
+                // @ts-ignore
                 window.location = '/dashboard/new';
               },
             },
@@ -173,6 +157,7 @@ export default function Welcome({ user }: WelcomeProps) {
               name: 'View All',
               buttonStyle: 'link',
               onClick: () => {
+                // @ts-ignore
                 window.location = '/dashboard/list/';
               },
             },
@@ -221,6 +206,7 @@ export default function Welcome({ user }: WelcomeProps) {
               ),
               buttonStyle: 'tertiary',
               onClick: () => {
+                // @ts-ignore
                 window.location = '/superset/sqllab';
               },
             },
@@ -228,13 +214,14 @@ export default function Welcome({ user }: WelcomeProps) {
               name: 'View All',
               buttonStyle: 'link',
               onClick: () => {
+                // @ts-ignore
                 window.location = 'superset/sqllab#search';
               },
             },
           ]}
         />
         <CardContainer>
-          <SavedQueries />
+          <SavedQueries user={user} queryFilter={queryFilter} />
         </CardContainer>
       </Panel>
       <Panel header={t('Charts')} key="4">
@@ -263,6 +250,7 @@ export default function Welcome({ user }: WelcomeProps) {
               ),
               buttonStyle: 'tertiary',
               onClick: () => {
+                // @ts-ignore
                 window.location = '/chart/add';
               },
             },
@@ -270,6 +258,7 @@ export default function Welcome({ user }: WelcomeProps) {
               name: 'View All',
               buttonStyle: 'link',
               onClick: () => {
+                // @ts-ignore
                 window.location = '/chart/list';
               },
             },
diff --git a/superset/queries/saved_queries/api.py b/superset/queries/saved_queries/api.py
index 794ab25..b37fc7d 100644
--- a/superset/queries/saved_queries/api.py
+++ b/superset/queries/saved_queries/api.py
@@ -106,7 +106,7 @@ class SavedQueryRestApi(BaseSupersetModelRestApi):
         "last_run_delta_humanized",
     ]
 
-    search_columns = ["id", "database", "label", "schema"]
+    search_columns = ["id", "database", "label", "schema", "created_by"]
     search_filters = {
         "id": [SavedQueryFavoriteFilter],
         "label": [SavedQueryAllTextFilter],


[incubator-superset] 25/33: more changes

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 8da172ca84f65b24b342c909414084841fc92efd
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Fri Oct 23 11:35:37 2020 -0700

    more changes
---
 superset-frontend/src/components/ListViewCard/index.tsx    | 4 ++--
 superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx | 8 ++++----
 superset-frontend/src/views/CRUD/welcome/ChartTable.tsx    | 3 ++-
 superset-frontend/src/views/CRUD/welcome/EmptyState.tsx    | 4 ++--
 superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx  | 2 --
 superset-frontend/src/views/CRUD/welcome/Welcome.tsx       | 8 ++++----
 6 files changed, 14 insertions(+), 15 deletions(-)

diff --git a/superset-frontend/src/components/ListViewCard/index.tsx b/superset-frontend/src/components/ListViewCard/index.tsx
index 674985b..a974889 100644
--- a/superset-frontend/src/components/ListViewCard/index.tsx
+++ b/superset-frontend/src/components/ListViewCard/index.tsx
@@ -143,13 +143,13 @@ const QueryData = styled.div`
   display: flex;
   flex-direction: row;
   justify-content: flex-start;
-  border-bottom: 1px solid #e0e0e0;
+  border-bottom: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
   .title {
     font-weight: 500;
     color: #b2b2b2;
   }
   .holder {
-    margin: 10px 10px 10px 10px;
+    margin: ${({ theme }) => theme.gridUnit * 2}px;
   }
 `;
 
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index bb21c58..63a1455 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -56,8 +56,8 @@ interface ActivityData {
 }
 
 const ActivityContainer = styled.div`
-  margin-left: 10px;
-  margin-top: -15px;
+  margin-left: ${({ theme }) => theme.gridUnit * 2}px;
+  margin-top: ${({ theme }) => theme.gridUnit * -4}px;
   display: grid;
   grid-template-columns: repeat(auto-fit, minmax(31%, max-content));
   ${[mq[3]]} {
@@ -74,8 +74,8 @@ const ActivityContainer = styled.div`
   padding: ${({ theme }) => theme.gridUnit * 2}px
     ${({ theme }) => theme.gridUnit * 4}px;
   .ant-card-meta-avatar {
-    margin-top: 10px;
-    margin-left: 5px;
+    margin-top: ${({ theme }) => theme.gridUnit * 3}px;
+    margin-left: ${({ theme }) => theme.gridUnit * 2}px;
   }
   .ant-card-meta-title {
     font-weight: ${({ theme }) => theme.typography.weights.bold};
diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
index a54d5a3..e12e204 100644
--- a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
@@ -125,7 +125,8 @@ function ChartTable({
           {
             name: (
               <IconContainer>
-                <Icon name="plus-small" /> Chart{' '}
+                <Icon name="plus-small" />
+                {t('Chart')}
               </IconContainer>
             ),
             buttonStyle: 'tertiary',
diff --git a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
index 21a37ff..fe2d03e 100644
--- a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
@@ -60,7 +60,7 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
         : t(`${tableName.toLowerCase()}`)
     } yet`}</div>
   );
-  const span = (
+  const recent = (
     <div className="no-recents">
       {t(`Recently ${tab?.toLowerCase()} charts, dashboards, and saved queries will
       appear here`)}
@@ -71,7 +71,7 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
     return (
       <Empty
         image={`/static/assets/images/${tableIcon[tableName]}`}
-        description={tableName === 'RECENTS' ? span : mine}
+        description={tableName === 'RECENTS' ? recent : mine}
       >
         {tableName !== 'RECENTS' && (
           <ButtonContainer>
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
index 43646d4..563ac14 100644
--- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -123,7 +123,6 @@ const SavedQueries = ({
       {canEdit && (
         <Menu.Item
           onClick={() => {
-            // @ts-ignore
             window.location.href = `/superset/sqllab?savedQueryId=${query.id}`;
           }}
         >
@@ -201,7 +200,6 @@ const SavedQueries = ({
             name: 'View All »',
             buttonStyle: 'link',
             onClick: () => {
-              // @ts-ignore
               window.location.href = '/savedqueryview/list';
             },
           },
diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index effe8cb..f35effc 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -75,17 +75,17 @@ const WelcomeContainer = styled.div`
 export default function Welcome({ user }: WelcomeProps) {
   return (
     <WelcomeContainer>
-      <Collapse defaultActiveKey={['1']} ghost>
+      <Collapse defaultActiveKey={['1', '2', '3', '4']} ghost>
         <Panel header={t('Recents')} key="1">
           <ActivityTable user={user} />
         </Panel>
-        <Panel header={t('Dashboards')} key="1">
+        <Panel header={t('Dashboards')} key="2">
           <DashboardTable user={user} />
         </Panel>
-        <Panel header={t('Saved Queries')} key="1">
+        <Panel header={t('Saved Queries')} key="3">
           <SavedQueries user={user} />
         </Panel>
-        <Panel header={t('Charts')} key="1">
+        <Panel header={t('Charts')} key="4">
           <ChartTable user={user} />
         </Panel>
       </Collapse>


[incubator-superset] 08/33: add icon

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit b029d5efbe19d30f3786fd90d428c3cb2e325cc4
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Fri Oct 2 16:57:08 2020 -0700

    add icon
---
 superset-frontend/src/components/ListViewCard/index.tsx | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/superset-frontend/src/components/ListViewCard/index.tsx b/superset-frontend/src/components/ListViewCard/index.tsx
index 5e06e79..eb2bbab 100644
--- a/superset-frontend/src/components/ListViewCard/index.tsx
+++ b/superset-frontend/src/components/ListViewCard/index.tsx
@@ -154,6 +154,7 @@ interface CardProps {
   actions: React.ReactNode;
   showImg?: boolean;
   rows?: number | string;
+  avatar?: string;
 }
 
 function ListViewCard({
@@ -166,7 +167,7 @@ function ListViewCard({
   coverLeft,
   coverRight,
   actions,
-  avatar,
+  avatar = 'nav-dashboard',
   loading,
   imgPosition = 'top',
   showImg = true,
@@ -254,7 +255,7 @@ function ListViewCard({
             </>
           }
           description={description}
-          avatar={<Icon name={avatar} />}
+          avatar={<IconComponent />}
         />
       )}
     </StyledCard>


[incubator-superset] 26/33: update tests and clear typescript errors

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 065ad3c1778541467e645117285df1259316e9fb
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Fri Oct 23 22:44:10 2020 -0700

    update tests and clear typescript errors
---
 .../views/CRUD/welcome/ChartTable_spec.tsx         |  8 +--
 .../views/CRUD/welcome/EmptyState_spec.tsx         | 76 +++++++++++++++++++---
 .../views/CRUD/welcome/SavedQueries_spec.tsx       | 20 ++++--
 .../views/CRUD/welcome/Welcome_spec.tsx            |  3 +-
 .../src/views/CRUD/dashboard/DashboardCard.tsx     |  2 +-
 .../src/views/CRUD/welcome/EmptyState.tsx          |  5 +-
 6 files changed, 89 insertions(+), 25 deletions(-)

diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx
index 7a39e9f..aa024d8 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx
@@ -31,7 +31,6 @@ const mockStore = configureStore([thunk]);
 const store = mockStore({});
 
 const chartsEndpoint = 'glob:*/api/v1/chart/?*';
-// fetchMock.get(chartsEndpoint, { result: mockDashboards });
 
 const mockCharts = [...new Array(3)].map((_, i) => ({
   changed_on_utc: new Date().toISOString(),
@@ -44,15 +43,11 @@ const mockCharts = [...new Array(3)].map((_, i) => ({
   thumbnail_url: '/thumbnail',
 }));
 
-/* fetchMock.get(chartsEndpoint, {
-  result: [],
-});
-*/
 fetchMock.get(chartsEndpoint, {
   result: mockCharts,
 });
 
-describe('DashboardTable', () => {
+describe('ChartTable', () => {
   beforeEach(fetchMock.resetHistory);
 
   const mockedProps = {};
@@ -67,6 +62,7 @@ describe('DashboardTable', () => {
   it('it renders', () => {
     expect(wrapper.find(ChartTable)).toExist();
   });
+
   it('fetches chart favorites and renders chart cards ', () => {
     expect(fetchMock.calls(chartsEndpoint)).toHaveLength(1);
     expect(wrapper.find(ChartCard)).toExist();
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx
index b4eb6d1..7eaa234 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx
@@ -18,18 +18,78 @@
  */
 import React from 'react';
 import { styledMount as mount } from 'spec/helpers/theming';
-import { act } from 'react-dom/test-utils';
-
-import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
 import EmptyState from 'src/views/CRUD/welcome/EmptyState';
 
 describe('EmptyState', () => {
-  it('it renders an favorite dashboard empty state', () => {
-    const props = {
+  const variants = [
+    {
       tab: 'Favorite',
       tableName: 'DASHBOARDS',
-    };
-    const wrapper = mount(<EmptyState {...props} />);
-    expect(wrapper).toExist();
+    },
+    {
+      tab: 'Mine',
+      tableName: 'DASHBOARDS',
+    },
+    {
+      tab: 'Favorite',
+      tableName: 'CHARTS',
+    },
+    {
+      tab: 'Mine',
+      tableName: 'CHARTS',
+    },
+    {
+      tab: 'Favorite',
+      tableName: 'SAVED_QUERIES',
+    },
+    {
+      tab: 'Mine',
+      tableName: 'SAVED_QUEREIS',
+    },
+  ];
+  const recents = [
+    {
+      tab: 'Viewed',
+      tableName: 'RECENTS',
+    },
+    {
+      tab: 'Edited',
+      tableName: 'RECENTS',
+    },
+    {
+      tab: 'Created',
+      tableName: 'RECENTS',
+    },
+  ];
+  variants.forEach(variant => {
+    it(`it renders an ${variant.tab} ${variant.tableName} empty state`, () => {
+      const wrapper = mount(<EmptyState {...variant} />);
+      expect(wrapper).toExist();
+      const textContainer = wrapper.find('.ant-empty-description');
+      expect(textContainer.text()).toEqual(
+        variant.tab === 'Favorite'
+          ? "You don't have any favorites yet!"
+          : `No ${
+              variant.tableName === 'SAVED_QUERIES'
+                ? 'saved queries'
+                : variant.tableName.toLowerCase()
+            } yet`,
+      );
+      console.log('wrapper', wrapper.debug());
+      console.log('wrapper', wrapper.find('ant-empty-image').children().debug());
+      //expect(wrapper.find('ant-empty-image').children()).toHaveLength(1);
+      expect(wrapper.find('button')).toHaveLength(1);
+    });
+  });
+  recents.forEach(recent => {
+    it(`it renders an ${recent.tab} ${recent.tableName} empty state`, () => {
+      const wrapper = mount(<EmptyState {...recent} />);
+      expect(wrapper).toExist();
+      const textContainer = wrapper.find('.ant-empty-description');
+      expect(wrapper.find('.ant-empty-image').children()).toHaveLength(1);
+      expect(textContainer.text()).toContain(
+        `Recently ${recent.tab.toLowerCase()} charts, dashboards, and saved queries will appear here`,
+      );
+    });
   });
 });
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
index 28173f2..015e4eb 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
@@ -31,7 +31,8 @@ import SavedQueries from 'src/views/CRUD/welcome/SavedQueries';
 const mockStore = configureStore([thunk]);
 const store = mockStore({});
 
-const queriesEndpoint = 'glob:*/api/v1/saved_queries/?*';
+const queriesEndpoint = 'glob:*/api/v1/saved_query/?*';
+const savedQueriesInfo = 'glob:*/api/v1/saved_query/_info';
 
 const mockqueries = [...new Array(3)].map((_, i) => ({
   created_by: {
@@ -64,6 +65,10 @@ fetchMock.get(queriesEndpoint, {
   result: mockqueries,
 });
 
+fetchMock.get(savedQueriesInfo, {
+  permissions: ['can_list', 'can_edit', 'can_delete'],
+});
+
 describe('SavedQueries', () => {
   const savedQueryProps = {
     user: {
@@ -82,19 +87,22 @@ describe('SavedQueries', () => {
     expect(wrapper.find(SavedQueries)).toExist();
   });
 
-  it('it renders a submenu with clickable tabls and buttons', async () => {
+  it('it renders a submenu with clickable tables and buttons', async () => {
     expect(wrapper.find(SubMenu)).toExist();
     expect(wrapper.find('MenuItem')).toHaveLength(2);
-    expect(wrapper.find('Button')).toHaveLength(4);
+    console.log('button', wrapper.find('button').length);
     act(() => {
       wrapper.find('MenuItem').at(1).simulate('click');
     });
+
+    console.log('menu item', wrapper.find('MenuItem').at(1).debug());
     await waitForComponentToPaint(wrapper);
-    expect(fetchMock.calls(/chart\/\?q/)).toHaveLength(1);
+    expect(fetchMock.calls(/saved_query\/\?q/)).toHaveLength(1);
   });
 
-  it('fetches queries favorites and renders chart cards', () => {
-    expect(fetchMock.calls(/chart\/\?q/)).toHaveLength(1);
+  it('fetches queries favorites and renders listviewcard cards', () => {
+    expect(fetchMock.calls(/saved_query\/\?q/)).toHaveLength(1);
     expect(wrapper.find('ListViewCard')).toExist();
+    console.log('wrapper', wrapper);
   });
 });
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
index 38891c0..fa1778d 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
@@ -37,8 +37,7 @@ describe('Welcome', () => {
   it('is renders', () => {
     expect(wrapper.find(Welcome)).toExist();
   });
-  it('renders first submenu on page load', () => {
-    expect(wrapper.find('SubMenu')).toHaveLength(1);
+  it('renders all panels on the page on page load', () => {
     expect(wrapper.find('PanelContent')).toHaveLength(4);
   });
 });
diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
index 2a09f70..2469d64 100644
--- a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
+++ b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
@@ -113,7 +113,7 @@ function DashboardCard({
   );
   return (
     <ListViewCard
-      loading={dashboard.loading}
+      loading={dashboard.loading || false}
       title={dashboard.dashboard_title}
       titleRight={<Label>{dashboard.published ? 'published' : 'draft'}</Label>}
       url={bulkSelectEnabled ? undefined : dashboard.url}
diff --git a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
index fe2d03e..381a08d 100644
--- a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
@@ -62,8 +62,9 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
   );
   const recent = (
     <div className="no-recents">
-      {t(`Recently ${tab?.toLowerCase()} charts, dashboards, and saved queries will
-      appear here`)}
+      {t(
+        `Recently ${tab?.toLowerCase()} charts, dashboards, and saved queries will appear here`,
+      )}
     </div>
   );
   // Mine and Recent Activity(all tabs) tab empty state


[incubator-superset] 03/33: more adding and slicing

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 5e2b9a4fbff63072a094338dad605098bd92d608
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Wed Sep 23 13:27:37 2020 -0700

    more adding and slicing
---
 .../src/components/ListViewCard/index.tsx          |  51 ++--
 .../src/views/CRUD/dashboard/DashboardCard.tsx     | 168 +++++++++++
 .../src/views/CRUD/dashboard/DashboardList.tsx     |  92 +-----
 .../src/views/CRUD/welcome/ChartTable.tsx          | 148 +++++++++
 .../src/views/CRUD/welcome/DashboardTable.tsx      | 329 +++++++--------------
 .../src/views/CRUD/welcome/SavedQueries.tsx        |  23 +-
 .../src/views/CRUD/welcome/Welcome.tsx             | 125 +++-----
 superset/queries/api.py                            |   3 +
 superset/views/api.py                              |   2 +
 9 files changed, 531 insertions(+), 410 deletions(-)

diff --git a/superset-frontend/src/components/ListViewCard/index.tsx b/superset-frontend/src/components/ListViewCard/index.tsx
index 4a0cf53..f07970c 100644
--- a/superset-frontend/src/components/ListViewCard/index.tsx
+++ b/superset-frontend/src/components/ListViewCard/index.tsx
@@ -152,6 +152,8 @@ interface CardProps {
   coverLeft?: React.ReactNode;
   coverRight?: React.ReactNode;
   actions: React.ReactNode;
+  showImg: boolean;
+  rows?: number | string;
 }
 
 function ListViewCard({
@@ -166,31 +168,40 @@ function ListViewCard({
   actions,
   loading,
   imgPosition = 'top',
+  showImg = true,
+  rows,
 }: CardProps) {
   return (
     <StyledCard
       data-test="styled-card"
       cover={
-        <Cover>
-          <a href={url}>
-            <div className="gradient-container">
-              <ImageLoader
-                src={imgURL}
-                fallback={imgFallbackURL}
-                isLoading={loading}
-                position={imgPosition}
-              />
-            </div>
-          </a>
-          <CoverFooter className="cover-footer">
-            {!loading && coverLeft && (
-              <CoverFooterLeft>{coverLeft}</CoverFooterLeft>
-            )}
-            {!loading && coverRight && (
-              <CoverFooterRight>{coverRight}</CoverFooterRight>
-            )}
-          </CoverFooter>
-        </Cover>
+        showImg ? (
+          <Cover>
+            <a href={url}>
+              <div className="gradient-container">
+                <ImageLoader
+                  src={imgURL}
+                  fallback={imgFallbackURL}
+                  isLoading={loading}
+                  position={imgPosition}
+                />
+              </div>
+            </a>
+            <CoverFooter className="cover-footer">
+              {!loading && coverLeft && (
+                <CoverFooterLeft>{coverLeft}</CoverFooterLeft>
+              )}
+              {!loading && coverRight && (
+                <CoverFooterRight>{coverRight}</CoverFooterRight>
+              )}
+            </CoverFooter>
+          </Cover>
+        ) : (
+          <>
+            <div>Rows</div>
+            <div>{rows}</div>
+          </>
+        )
       }
     >
       {loading && (
diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
new file mode 100644
index 0000000..46d3088
--- /dev/null
+++ b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
@@ -0,0 +1,168 @@
+import React from 'react';
+import { SupersetClient, t } from '@superset-ui/core';
+import rison from 'rison';
+import { Dropdown, Menu } from 'src/common/components';
+import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
+import ListViewCard from 'src/components/ListViewCard';
+import Icon from 'src/components/Icon';
+import Label from 'src/components/Label';
+import FacePile from 'src/components/FacePile';
+import FaveStar from 'src/components/FaveStar';
+import Owner from 'src/types/Owner';
+
+import { createErrorHandler } from 'src/views/CRUD/utils';
+import { useFavoriteStatus } from 'src/views/CRUD/hooks';
+
+const FAVESTAR_BASE_URL = '/superset/favstar/Dashboard';
+
+interface Dashboard {
+  changed_by_name: string;
+  changed_by_url: string;
+  changed_on_delta_humanized: string;
+  changed_by: string;
+  dashboard_title: string;
+  slice_name: string;
+  id: number;
+  published: boolean;
+  url: string;
+  thumbnail_url: string;
+  owners: Owner[];
+  loading: boolean;
+}
+
+interface DashboardCardProps {
+  isChart?: boolean;
+  dashboard: Dashboard;
+  hasPerm: (name: string) => boolean;
+  bulkSelectEnabled: boolean;
+  refreshData: () => void;
+  addDangerToast: (msg: string) => void;
+  addSuccessToast: (msg: string) => void;
+  openDashboardEditModal?: (d: Dashboard) => void;
+}
+
+function DashboardCard({
+  isChart,
+  dashboard,
+  hasPerm,
+  bulkSelectEnabled,
+  refreshData,
+  addDangerToast,
+  addSuccessToast,
+  openDashboardEditModal,
+}: DashboardCardProps) {
+  const canEdit = hasPerm('can_edit');
+  const canDelete = hasPerm('can_delete');
+  const canExport = hasPerm('can_mulexport');
+  const [favoriteStatusRef, fetchFaveStar, saveFaveStar] = useFavoriteStatus(
+    {},
+    FAVESTAR_BASE_URL,
+    addDangerToast,
+  );
+
+  function handleDashboardDelete({
+    id,
+    dashboard_title: dashboardTitle,
+  }: Dashboard) {
+    return SupersetClient.delete({
+      endpoint: `/api/v1/dashboard/${id}`,
+    }).then(
+      () => {
+        refreshData();
+        addSuccessToast(t('Deleted: %s', dashboardTitle));
+      },
+      createErrorHandler(errMsg =>
+        addDangerToast(
+          t('There was an issue deleting %s: %s', dashboardTitle, errMsg),
+        ),
+      ),
+    );
+  }
+
+  function handleBulkDashboardExport(dashboardsToExport: Dashboard[]) {
+    return window.location.assign(
+      `/api/v1/dashboard/export/?q=${rison.encode(
+        dashboardsToExport.map(({ id }) => id),
+      )}`,
+    );
+  }
+
+  const cardTitle = isChart ? dashboard.slice_name : dashboard.dashboard_title;
+
+  const menu = (
+    <Menu>
+      {canDelete && (
+        <Menu.Item>
+          <ConfirmStatusChange
+            title={t('Please Confirm')}
+            description={
+              <>
+                {t('Are you sure you want to delete')} <b>{cardTitle}</b>?
+              </>
+            }
+            onConfirm={() => handleDashboardDelete(dashboard)}
+          >
+            {confirmDelete => (
+              <div
+                role="button"
+                tabIndex={0}
+                className="action-button"
+                onClick={confirmDelete}
+              >
+                <ListViewCard.MenuIcon name="trash" /> Delete
+              </div>
+            )}
+          </ConfirmStatusChange>
+        </Menu.Item>
+      )}
+      {canExport && (
+        <Menu.Item
+          role="button"
+          tabIndex={0}
+          onClick={() => handleBulkDashboardExport([dashboard])}
+        >
+          <ListViewCard.MenuIcon name="share" /> Export
+        </Menu.Item>
+      )}
+      {canEdit && openDashboardEditModal && (
+        <Menu.Item
+          role="button"
+          tabIndex={0}
+          onClick={() =>
+            openDashboardEditModal && openDashboardEditModal(dashboard)
+          }
+        >
+          <ListViewCard.MenuIcon name="pencil" /> Edit
+        </Menu.Item>
+      )}
+    </Menu>
+  );
+  return (
+    <ListViewCard
+      loading={dashboard.loading}
+      title={dashboard.dashboard_title}
+      titleRight={<Label>{dashboard.published ? 'published' : 'draft'}</Label>}
+      url={bulkSelectEnabled ? undefined : dashboard.url}
+      imgURL={dashboard.thumbnail_url}
+      imgFallbackURL="/static/assets/images/dashboard-card-fallback.png"
+      description={t('Last modified %s', dashboard.changed_on_delta_humanized)}
+      coverLeft={<FacePile users={dashboard.owners || []} />}
+      actions={
+        <ListViewCard.Actions>
+          <FaveStar
+            itemId={dashboard.id}
+            fetchFaveStar={fetchFaveStar}
+            saveFaveStar={saveFaveStar}
+            isStarred={!!favoriteStatusRef.current[dashboard.id]}
+          />
+          <Dropdown overlay={menu}>
+            <Icon name="more-horiz" />
+          </Dropdown>
+        </ListViewCard.Actions>
+      }
+      showImg
+    />
+  );
+}
+
+export default DashboardCard;
diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx
index ac4d747..1fe8427 100644
--- a/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx
+++ b/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx
@@ -24,17 +24,15 @@ import { createFetchRelated, createErrorHandler } from 'src/views/CRUD/utils';
 import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
 import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
 import SubMenu, { SubMenuProps } from 'src/components/Menu/SubMenu';
-import FacePile from 'src/components/FacePile';
 import ListView, { ListViewProps, Filters } from 'src/components/ListView';
 import Owner from 'src/types/Owner';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
+import FacePile from 'src/components/FacePile';
 import Icon from 'src/components/Icon';
-import Label from 'src/components/Label';
 import FaveStar from 'src/components/FaveStar';
 import PropertiesModal from 'src/dashboard/components/PropertiesModal';
-import ListViewCard from 'src/components/ListViewCard';
-import { Dropdown, Menu } from 'src/common/components';
 import TooltipWrapper from 'src/components/TooltipWrapper';
+import DashboardCard from './DashboardCard';
 
 const PAGE_SIZE = 25;
 const FAVESTAR_BASE_URL = '/superset/favstar/Dashboard';
@@ -81,7 +79,6 @@ function DashboardList(props: DashboardListProps) {
     FAVESTAR_BASE_URL,
     props.addDangerToast,
   );
-
   const [dashboardToEdit, setDashboardToEdit] = useState<Dashboard | null>(
     null,
   );
@@ -419,82 +416,17 @@ function DashboardList(props: DashboardListProps) {
   ];
 
   function renderCard(dashboard: Dashboard & { loading: boolean }) {
-    const menu = (
-      <Menu>
-        {canDelete && (
-          <Menu.Item>
-            <ConfirmStatusChange
-              title={t('Please Confirm')}
-              description={
-                <>
-                  {t('Are you sure you want to delete')}{' '}
-                  <b>{dashboard.dashboard_title}</b>?
-                </>
-              }
-              onConfirm={() => handleDashboardDelete(dashboard)}
-            >
-              {confirmDelete => (
-                <div
-                  role="button"
-                  tabIndex={0}
-                  className="action-button"
-                  onClick={confirmDelete}
-                >
-                  <ListViewCard.MenuIcon
-                    data-test="dashboard-list-view-card-trash-icon"
-                    name="trash"
-                  />{' '}
-                  Delete
-                </div>
-              )}
-            </ConfirmStatusChange>
-          </Menu.Item>
-        )}
-        {canExport && (
-          <Menu.Item
-            role="button"
-            tabIndex={0}
-            onClick={() => handleBulkDashboardExport([dashboard])}
-          >
-            <ListViewCard.MenuIcon name="share" /> Export
-          </Menu.Item>
-        )}
-        {canEdit && (
-          <Menu.Item
-            data-test="dashboard-list-edit-option"
-            role="button"
-            tabIndex={0}
-            onClick={() => openDashboardEditModal(dashboard)}
-          >
-            <ListViewCard.MenuIcon name="edit-alt" /> Edit
-          </Menu.Item>
-        )}
-      </Menu>
-    );
-
     return (
-      <ListViewCard
-        loading={dashboard.loading}
-        title={dashboard.dashboard_title}
-        titleRight={
-          <Label>{dashboard.published ? 'published' : 'draft'}</Label>
-        }
-        url={bulkSelectEnabled ? undefined : dashboard.url}
-        imgURL={dashboard.thumbnail_url}
-        imgFallbackURL="/static/assets/images/dashboard-card-fallback.png"
-        description={t(
-          'Last modified %s',
-          dashboard.changed_on_delta_humanized,
-        )}
-        coverLeft={<FacePile users={dashboard.owners || []} />}
-        actions={
-          <ListViewCard.Actions>
-            {renderFaveStar(dashboard.id)}
-            <Dropdown overlay={menu}>
-              <Icon name="more-horiz" />
-            </Dropdown>
-          </ListViewCard.Actions>
-        }
+      <DashboardCard
+        {...{
+          dashboard,
+          hasPerm,
+          bulkSelectEnabled,
+          refreshData,
+          addDangerToast: props.addDangerToast,
+          addSuccessToast: props.addSuccessToast,
+          openDashboardEditModal,
+        }}
       />
     );
   }
diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
new file mode 100644
index 0000000..9b8529e
--- /dev/null
+++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
@@ -0,0 +1,148 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React, { useEffect } from 'react';
+import { t } from '@superset-ui/core';
+import { debounce } from 'lodash';
+import ListView, { FetchDataConfig } from 'src/components/ListView';
+import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
+import withToasts from 'src/messageToasts/enhancers/withToasts';
+import { User } from 'src/types/bootstrapTypes';
+import ListViewCard from 'src/components/ListViewCard';
+import { Dropdown, Menu } from 'src/common/components';
+import Icon from 'src/components/Icon';
+import Label from 'src/components/Label';
+import Owner from 'src/types/Owner';
+import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
+import DashboardCard from '../dashboard/DashboardCard';
+const PAGE_SIZE = 3;
+
+interface ChartTableProps {
+  addDangerToast: (message: string) => void;
+  addSuccessToast: (message: string) => void;
+  search: string;
+  chartFilter?: string;
+  user?: User;
+}
+
+interface Dashboard {
+  changed_by_name: string;
+  changed_by_url: string;
+  changed_on_delta_humanized: string;
+  changed_by: string;
+  dashboard_title: string;
+  slice_name: string;
+  id: number;
+  published: boolean;
+  url: string;
+  thumbnail_url: string;
+  owners: Owner[];
+  loading: boolean;
+}
+
+interface ChartTableState {
+  charts: Dashboard[];
+  chart_count: number;
+  loading: boolean;
+}
+
+export interface FilterValue {
+  col: string;
+  operator: string;
+  value: string | boolean | number | null | undefined;
+}
+
+export interface FetchDataConfig {
+  filters: FilterValue[];
+}
+
+function ChartTable({
+  chartFilter,
+  user,
+  addDangerToast,
+  addSuccessToast,
+}: ChartTableProps) {
+  const {
+    state: { loading, resourceCollection: charts, bulkSelectEnabled },
+    hasPerm,
+    refreshData,
+    fetchData,
+  } = useListViewResource<Dashboard>('chart', t('chart'), addDangerToast);
+  console.log('dashboardFilter', chartFilter);
+  const getFilters = () => {
+    const filters = [];
+
+    if (chartFilter === 'Mine') {
+      filters.push({
+        id: 'owners',
+        operator: 'rel_m_m',
+        value: `${user?.userId}`,
+      });
+    } else {
+      filters.push({
+        id: 'favorite', // API currently can't filter by favorite
+        operator: 'eq',
+        value: true,
+      });
+    }
+    // Do we need search?
+    /* filters.concat([
+      {
+        id: 'dashboard_title',
+        operator: 'ct',
+        value: search,
+      },
+    ]);
+    */
+    return filters;
+  };
+
+  useEffect(() => {
+    fetchData({
+      pageIndex: 0,
+      pageSize: PAGE_SIZE,
+      sortBy: [
+        {
+          id: 'changed_on_delta_humanized',
+          desc: true,
+        },
+      ],
+      filters: getFilters(),
+    });
+  }, [chartFilter]);
+  console.log("----charts: ", charts);
+  return (
+    <div>
+      {charts.map(e => (
+        <DashboardCard
+          {...{
+            dashboard: e,
+            hasPerm,
+            bulkSelectEnabled,
+            refreshData,
+            addDangerToast,
+            addSuccessToast,
+          }}
+          isChart
+        />
+      ))}
+    </div>
+  );
+}
+
+export default withToasts(ChartTable);
diff --git a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
index f0560e4..39c5503 100644
--- a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
@@ -16,27 +16,46 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React from 'react';
-import { t, SupersetClient } from '@superset-ui/core';
+import React, { useEffect } from 'react';
+import { t } from '@superset-ui/core';
 import { debounce } from 'lodash';
 import ListView, { FetchDataConfig } from 'src/components/ListView';
+import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
+import { User } from 'src/types/bootstrapTypes';
 import ListViewCard from 'src/components/ListViewCard';
-import { Dashboard } from 'src/types/bootstrapTypes';
 import { Dropdown, Menu } from 'src/common/components';
 import Icon from 'src/components/Icon';
 import Label from 'src/components/Label';
+import Owner from 'src/types/Owner';
 import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
-
+import DashboardCard from '../dashboard/DashboardCard';
 const PAGE_SIZE = 3;
 
-//const canEdit = hasPerm('can_edit');
-//const canDelete = hasPerm('can_delete');
-//const canExport = hasPerm('can_mulexport');
+// const canEdit = hasPerm('can_edit');
+// const canDelete = hasPerm('can_delete');
+// const canExport = hasPerm('can_mulexport');
 
 interface DashboardTableProps {
   addDangerToast: (message: string) => void;
-  search?: string;
+  addSuccessToast: (message: string) => void;
+  search: string;
+  dashboardFilter?: string;
+  user?: User;
+}
+
+interface Dashboard {
+  changed_by_name: string;
+  changed_by_url: string;
+  changed_on_delta_humanized: string;
+  changed_by: string;
+  dashboard_title: string;
+  id: number;
+  published: boolean;
+  url: string;
+  thumbnail_url: string;
+  owners: Owner[];
+  loading: boolean;
 }
 
 interface DashboardTableState {
@@ -45,232 +64,90 @@ interface DashboardTableState {
   loading: boolean;
 }
 
-class DashboardTable extends React.PureComponent<
-  DashboardTableProps,
-  DashboardTableState
-> {
-  columns = [
-    {
-      accessor: 'dashboard_title',
-      Header: 'Dashboard',
-      Cell: ({
-        row: {
-          original: { url, dashboard_title: dashboardTitle },
-        },
-      }: {
-        row: {
-          original: {
-            url: string;
-            dashboard_title: string;
-          };
-        };
-      }) => <a href={url}>{dashboardTitle}</a>,
-    },
-    {
-      accessor: 'changed_by.first_name',
-      Header: 'Modified By',
-      Cell: ({
-        row: {
-          original: { changed_by_name: changedByName, changedByUrl },
-        },
-      }: {
-        row: {
-          original: {
-            changed_by_name: string;
-            changedByUrl: string;
-          };
-        };
-      }) => <a href={changedByUrl}>{changedByName}</a>,
-    },
-    {
-      accessor: 'changed_on_delta_humanized',
-      Header: 'Modified',
-      Cell: ({
-        row: {
-          original: { changed_on_delta_humanized: changedOn },
-        },
-      }: {
-        row: {
-          original: {
-            changed_on_delta_humanized: string;
-          };
-        };
-      }) => <span className="no-wrap">{changedOn}</span>,
-    },
-  ];
-
-  initialSort = [{ id: 'changed_on_delta_humanized', desc: true }];
+export interface FilterValue {
+  col: string;
+  operator: string;
+  value: string | boolean | number | null | undefined;
+}
 
-  constructor(props: DashboardTableProps) {
-    super(props);
-    this.state = {
-      dashboards: [],
-      dashboard_count: 0,
-      loading: false,
-    };
-  }
+export interface FetchDataConfig {
+  filters: FilterValue[];
+}
 
-  componentDidMount() {
-    console.log('component did mount!!!');
-    this.fetchDataDebounced({
-      pageSize: PAGE_SIZE,
-      pageIndex: 0,
-      sortBy: this.initialSort,
-      filters: [],
-    });
-  }
+function DashboardTable({
+  dashboardFilter,
+  user,
+  addDangerToast,
+  addSuccessToast,
+  search,
+}: DashboardTableProps) {
+  const {
+    state: { loading, resourceCollection: dashboards, bulkSelectEnabled },
+    hasPerm,
+    refreshData,
+    fetchData,
+  } = useListViewResource<Dashboard>(
+    'dashboard',
+    t('dashboard'),
+    addDangerToast,
+  );
+  console.log('dashboardFilter', dashboardFilter);
+  const getFilters = () => {
+    const filters = [];
 
-  componentDidUpdate(prevProps: DashboardTableProps) {
-    if (prevProps.search !== this.props.search) {
-      this.fetchDataDebounced({
-        pageSize: PAGE_SIZE,
-        pageIndex: 0,
-        sortBy: this.initialSort,
-        filters: [],
+    if (dashboardFilter === 'Mine') {
+      filters.push({
+        id: 'owners',
+        operator: 'rel_m_m',
+        value: `${user?.userId}`,
+      });
+    } else {
+      filters.push({
+        id: 'favorite', // API currently can't filter by favorite
+        operator: 'eq',
+        value: true,
       });
     }
-  }
-
-  fetchData = ({ pageIndex, pageSize, sortBy, filters }: FetchDataConfig) => {
-    this.setState({ loading: true });
-    const filterExps = Object.keys(filters)
-      .map(fk => ({
-        col: fk,
-        opr: filters[fk].filterId,
-        value: filters[fk].filterValue,
-      }))
-      .concat(
-        this.props.search
-          ? [
-              {
-                col: 'dashboard_title',
-                opr: 'ct',
-                value: this.props.search,
-              },
-            ]
-          : [],
-      );
-
-    /*const { dashboardFilter, user} = this.props;
-    const filters = [];
+    filters.concat([
+      {
+        id: 'dashboard_title',
+        operator: 'ct',
+        value: search,
+      },
+    ]);
+    return filters;
+  };
 
-    if (dashboardFilter === "Mine") {
-      filters.push[{
-        {
-          col: "owners.id", // API does not allow filter by owner id
-          opr: "eq",
-          value: user.id
-        }
-      }];
-    } else {
-      filter.push[{
+  useEffect(() => {
+    fetchData({
+      pageIndex: 0,
+      pageSize: PAGE_SIZE,
+      sortBy: [
         {
-          col: "favorite", // API currently can't filter by favorite
-          opr: "eq",
-          value: true
-        }
-      }];
-    }*/
-
-    /*const menu = (
-      <Menu>
-        {canDelete && (
-          <Menu.Item>
-            <ConfirmStatusChange
-              title={t('Please Confirm')}
-              description={
-                <>
-                  {t('Are you sure you want to delete')}{' '}
-                </>
-              }
-              onConfirm={() => handleDashboardDelete(dashboard)}
-            >
-              {confirmDelete => (
-                <div
-                  role="button"
-                  tabIndex={0}
-                  className="action-button"
-                  onClick={confirmDelete}
-                >
-                  <ListViewCard.MenuIcon name="trash" /> Delete
-                </div>
-              )}
-            </ConfirmStatusChange>
-          </Menu.Item>
-        )}
-        {canExport && (
-          <Menu.Item
-            role="button"
-            tabIndex={0}
-            onClick={() => handleBulkDashboardExport([dashboard])}
-          >
-            <ListViewCard.MenuIcon name="share" /> Export
-          </Menu.Item>
-        )}
-        {canEdit && (
-          <Menu.Item
-            role="button"
-            tabIndex={0}
-            onClick={() => openDashboardEditModal(dashboard)}
-          >
-            <ListViewCard.MenuIcon name="pencil" /> Edit
-          </Menu.Item>
-        )}
-      </Menu>
-    ); */
-
-    console.log('sortBy', sortBy);
-    const queryParams = JSON.stringify({
-      order_column: sortBy[0].id,
-      order_direction: sortBy[0].desc ? 'desc' : 'asc',
-      page: pageIndex,
-      page_size: pageSize,
-      //filters,
-      ...(filterExps.length ? { filters: filterExps } : {}),
+          id: 'changed_on_delta_humanized',
+          desc: true,
+        },
+      ],
+      filters: getFilters(),
     });
-    console.log('hello!!!');
-    return SupersetClient.get({
-      endpoint: `/api/v1/dashboard/?q=${queryParams}`,
-    })
-      .then(({ json }) => {
-        console.log('json', json);
-        this.setState({ dashboards: json.result, dashboard_count: json.count });
-        console.log('this.state', this.state);
-      })
-      .catch(response => {
-        if (response.status === 401) {
-          this.props.addDangerToast(
-            t(
-              "You don't have the necessary permissions to load dashboards. Please contact your administrator.",
-            ),
-          );
-        } else {
-          this.props.addDangerToast(
-            t('An error occurred while fetching Dashboards'),
-          );
-        }
-      })
-      .finally(() => this.setState({ loading: false }));
-  };
-
-  // sort-comp disabled because of conflict with no-use-before-define rule
-  // eslint-disable-next-line react/sort-comp
-  fetchDataDebounced = debounce(this.fetchData, 200);
-
-  render() {
-    return (
-      <div>
-        {this.state.dashboards.map(e => (
-          <ListViewCard
-            title={e.dashboard_title}
-            loading={this.state.loading}
-            titleRight={<Label>{e.published ? 'published' : 'draft'}</Label>}
-            description={t('Last modified %s', e.changed_on_delta_humanized)}
-          />
-        ))}
-      </div>
-    );
-  }
+  }, [dashboardFilter]);
+
+  return (
+    <div>
+      {dashboards.map(e => (
+        <DashboardCard
+          {...{
+            dashboard: e,
+            hasPerm,
+            bulkSelectEnabled,
+            refreshData,
+            addDangerToast,
+            addSuccessToast,
+          }}
+        />
+      ))}
+    </div>
+  );
 }
 
 export default withToasts(DashboardTable);
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
index 62e9e0e..7f261cc 100644
--- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -29,32 +29,43 @@ class SavedQueries extends React.PureComponent {
       queries: [],
     };
   }
-  componentDidMount(){
+  componentDidMount() {
     this.fetchData();
   }
-  fetchData = () => {
+  fetchData = async () => {
     try {
-      const { json } = SupersetClient.get({
+      const ids = [];
+      const { json } = await SupersetClient.get({
         endpoint: `/api/v1/query/`,
       });
+      /*(json.ids.forEach(id => {
+        const { json } = SupersetClient.get({
+          endpoint: `/api/v1/query/${id}`,
+        });
+        console.log('data', json);
+      });*/
+      console.log('json.result', json);
       this.setState({ queries: json.result });
     } catch (e) {
       return console.log(e);
     }
   };
   render() {
+    console.log('q', this.state.queries)
     return (
       <div>
         {this.state.queries.map(q => (
           <ListViewCard
-            title={q.database_name}
+            title={q.database.database_name}
+            rows={q.rows}
             loading={false}
             description={t('Last run ', q.end_time)}
+            showImg={false}
           />
         ))}
       </div>
-    )
+    );
   }
 }
 
-export default SavedQueries; 
\ No newline at end of file
+export default SavedQueries;
diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index 5e311dd..337b4e1 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -31,6 +31,7 @@ import { useQueryParam, StringParam, QueryParamConfig } from 'use-query-params';
 import { User } from 'src/types/bootstrapTypes';
 import RecentActivity from 'src/profile/components/RecentActivity';
 import Favorites from 'src/profile/components/Favorites';
+import ChartTable from './ChartTable';
 import SavedQueries from './SavedQueries';
 import DashboardTable from './DashboardTable';
 
@@ -60,7 +61,7 @@ function useSyncQueryState(
 }
 
 function ding(e: any) {
-  console.log('event',e);
+  console.log('event', e);
 }
 
 export default function Welcome({ user }: WelcomeProps) {
@@ -70,24 +71,13 @@ export default function Welcome({ user }: WelcomeProps) {
     'all',
   );
   const [dashboardFilter, setDashboardFilter] = useState('Favorite');
+  const [chartFilter, setChartFilter] = useState('Favorite');
   const [searchQuery, setSearchQuery] = useSyncQueryState(
     'search',
     StringParam,
     '',
   );
-
-  const onFormControlChange = useCallback(
-    (e: React.FormEvent<FormControl & FormControlProps>) => {
-      const { value } = e.currentTarget;
-      setSearchQuery((value as string) ?? '');
-    },
-    [],
-  );
-
-  const onTabsSelect = useCallback((e: any) => {
-    setActiveTab(e as string);
-  }, []);
-
+  console.log('user', user);
   return (
     <Collapse defaultActiveKey={['1']}>
       <Panel header={t('Recents')} key="1">
@@ -117,7 +107,7 @@ export default function Welcome({ user }: WelcomeProps) {
       </Panel>
 
       <Panel header={t('Dashboards')} key="2">
-      <SubMenu
+        <SubMenu
           activeChild={dashboardFilter}
           name=""
           // eslint-disable-next-line react/no-children-prop
@@ -142,74 +132,53 @@ export default function Welcome({ user }: WelcomeProps) {
           // @ts-ignore React bootstrap types aren't quite right here
           onChange={e => setSearchQuery(e.currentTarget.value)}
         />
-        <DashboardTable search={searchQuery} filter={dashboardFilter} />
+        <DashboardTable
+          search={searchQuery}
+          dashboardFilter={dashboardFilter}
+          user={user}
+        />
       </Panel>
 
-      <Panel header={t('Saved Queries')} key="3" >
+      <Panel header={t('Saved Queries')} key="3">
+        <SubMenu
+          activeChild={dashboardFilter}
+          name=""
+          // eslint-disable-next-line react/no-children-prop
+          children={[
+            {
+              name: 'Favorite',
+              label: t('Favorite'),
+              onClick: () => setChartFilter('Favorite'),
+            },
+            {
+              name: 'Mine',
+              label: t('Mine'),
+              onClick: () => setChartFilter('Mine'),
+            },
+          ]}
+        />
         <SavedQueries />
       </Panel>
-      <Panel header={t('Charts')} key="4" >
-        Stuff here!
+      <Panel header={t('Charts')} key="4">
+        <SubMenu
+          activeChild={chartFilter}
+          name=""
+          // eslint-disable-next-line react/no-children-prop
+          children={[
+            {
+              name: 'Favorite',
+              label: t('Favorite'),
+              onClick: () => setChartFilter('Favorite'),
+            },
+            {
+              name: 'Mine',
+              label: t('Mine'),
+              onClick: () => setChartFilter('Mine'),
+            },
+          ]}
+        />
+        <ChartTable chartFilter={chartFilter} user={user} />
       </Panel>
     </Collapse>
-    /*
-    <div className="container welcome">
-      <Tabs
-        activeKey={activeTab}
-        onSelect={onTabsSelect}
-        id="uncontrolled-tab-example"
-      >
-        <Tab eventKey="all" title={t('Dashboards')}>
-          <Panel>
-            <Panel.Body>
-              <Row>
-                <Col md={8}>
-                  <h2>{t('Dashboards')}</h2>
-                </Col>
-                <Col md={4}>
-                  <FormControl
-                    type="text"
-                    bsSize="sm"
-                    style={{ marginTop: '25px' }}
-                    placeholder="Search"
-                    value={searchQuery}
-                    onChange={onFormControlChange}
-                  />
-                </Col>
-              </Row>
-              <hr />
-              <DashboardTable search={searchQuery} />
-            </Panel.Body>
-          </Panel>
-        </Tab>
-        <Tab eventKey="recent" title={t('Recently Viewed')}>
-          <Panel>
-            <Panel.Body>
-              <Row>
-                <Col md={8}>
-                  <h2>{t('Recently Viewed')}</h2>
-                </Col>
-              </Row>
-              <hr />
-              <RecentActivity user={user} />
-            </Panel.Body>
-          </Panel>
-        </Tab>
-        <Tab eventKey="favorites" title={t('Favorites')}>
-          <Panel>
-            <Panel.Body>
-              <Row>
-                <Col md={8}>
-                  <h2>{t('Favorites')}</h2>
-                </Col>
-              </Row>
-              <hr />
-              <Favorites user={user} />
-            </Panel.Body>
-          </Panel>
-        </Tab>
-      </Tabs>
-    </div>
-    */
   );
 }
diff --git a/superset/queries/api.py b/superset/queries/api.py
index 0f368d3..ce4fca5 100644
--- a/superset/queries/api.py
+++ b/superset/queries/api.py
@@ -42,6 +42,9 @@ class QueryRestApi(BaseSupersetModelRestApi):
         "status",
         "start_time",
         "end_time",
+        "rows",
+        "tmp_table_name",
+        "tracking_url",
     ]
     show_columns = [
         "client_id",
diff --git a/superset/views/api.py b/superset/views/api.py
index a5090b3..19686a6 100644
--- a/superset/views/api.py
+++ b/superset/views/api.py
@@ -42,9 +42,11 @@ class Api(BaseSupersetView):
 
         raises SupersetSecurityException: If the user cannot access the resource
         """
+        print("hello")
         query_context = QueryContext(**json.loads(request.form["query_context"]))
         query_context.raise_for_access()
         payload_json = query_context.get_payload()
+        print("playload", payload_json)
         return json.dumps(
             payload_json, default=utils.json_int_dttm_ser, ignore_nan=True
         )


[incubator-superset] 13/33: remove consoles

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit faf395e17cfb857988cd6bba02c7dfffb369b675
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Thu Oct 8 14:41:08 2020 -0700

    remove consoles
---
 superset-frontend/src/views/CRUD/utils.tsx | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/superset-frontend/src/views/CRUD/utils.tsx b/superset-frontend/src/views/CRUD/utils.tsx
index bd6e9f0..6caabb9 100644
--- a/superset-frontend/src/views/CRUD/utils.tsx
+++ b/superset-frontend/src/views/CRUD/utils.tsx
@@ -63,16 +63,8 @@ export const createBatchMethod = (queryParams: string, created?: string) => {
       SupersetClient.get({ endpoint: `/api/v1/saved_query/?q=${queryParams}` }),
     );
   return Promise.all(baseBatch).then(([dashboardRes, chartRes, savedQuery]) => {
-    console.log('dashboard', dashboardRes);
-    console.log('chartRes', chartRes);
-    console.log('savedQuery', savedQuery);
     const results = [];
     const ifQuery = savedQuery ? savedQuery.json?.result.slice(0, 3) : [];
-    console.log('-----slices--------');
-    console.log(dashboardRes?.json?.result.slice(0, 3));
-    console.log(chartRes?.json?.result.slice(0, 3));
-    console.log(ifQuery);
-    console.log('-----END Slices--------');
     results.push(
       ...[
         ...dashboardRes.json?.result.slice(0, 3),
@@ -80,7 +72,6 @@ export const createBatchMethod = (queryParams: string, created?: string) => {
         ...ifQuery,
       ],
     );
-    console.log('result', results);
     return results;
   });
 };


[incubator-superset] 06/33: more updates

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit fbd15110d25c5c1b41ffee33ae3d307bf3ba2796
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Thu Oct 1 19:52:23 2020 -0700

    more updates
---
 .../src/components/ListViewCard/index.tsx          |   2 +-
 superset-frontend/src/components/Menu/SubMenu.tsx  |   5 -
 .../src/views/CRUD/dashboard/DashboardCard.tsx     |   2 +-
 .../src/views/CRUD/welcome/ActivityTable.tsx       | 123 +++++++++++++++++++++
 .../src/views/CRUD/welcome/DashboardTable.tsx      |  29 +----
 .../src/views/CRUD/welcome/SavedQueries.tsx        |  33 +++---
 .../src/views/CRUD/welcome/Welcome.tsx             |  42 +++++--
 superset/views/api.py                              |   2 -
 8 files changed, 181 insertions(+), 57 deletions(-)

diff --git a/superset-frontend/src/components/ListViewCard/index.tsx b/superset-frontend/src/components/ListViewCard/index.tsx
index f07970c..fc62f7b 100644
--- a/superset-frontend/src/components/ListViewCard/index.tsx
+++ b/superset-frontend/src/components/ListViewCard/index.tsx
@@ -152,7 +152,7 @@ interface CardProps {
   coverLeft?: React.ReactNode;
   coverRight?: React.ReactNode;
   actions: React.ReactNode;
-  showImg: boolean;
+  showImg?: boolean;
   rows?: number | string;
 }
 
diff --git a/superset-frontend/src/components/Menu/SubMenu.tsx b/superset-frontend/src/components/Menu/SubMenu.tsx
index 381ac2e..df7e501 100644
--- a/superset-frontend/src/components/Menu/SubMenu.tsx
+++ b/superset-frontend/src/components/Menu/SubMenu.tsx
@@ -66,13 +66,8 @@ type MenuClickHandler = React.MouseEventHandler<MenuItem>;
 type MenuChild = {
   label: string;
   name: string;
-<<<<<<< HEAD
   url: string;
   usesRouter?: boolean;
-=======
-  url?: string;
-  onClick?: any;
->>>>>>> step 1: broken stuff!
 };
 
 export interface ButtonProps {
diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
index 46d3088..f42de20 100644
--- a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
+++ b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
@@ -21,7 +21,7 @@ interface Dashboard {
   changed_on_delta_humanized: string;
   changed_by: string;
   dashboard_title: string;
-  slice_name: string;
+  slice_name?: string;
   id: number;
   published: boolean;
   url: string;
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
new file mode 100644
index 0000000..9203848
--- /dev/null
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -0,0 +1,123 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React, { useEffect, useState } from 'react';
+import { styled, SupersetClient, t } from '@superset-ui/core';
+import rison from 'rison';
+import moment from 'moment';
+import { addDangerToast } from 'src/messageToasts/actions';
+import { createBatchMethod, createErrorHandler } from '../utils';
+
+interface MapProps {
+  item_title: string;
+  slice_name: string;
+  time: string;
+  changed_on_utc: string;
+  user: {
+    userId: string | number;
+  };
+  activityFilter: string;
+  item_url: string;
+}
+
+const Cards = styled.div`
+  width: 200px;
+  padding: 15px;
+  border: 1px solid #f0f0f0;
+`;
+
+const filters = {
+  // Chart and dashbaord uses same filters
+  // for edited and created
+  edited: [
+    {
+      col: 'changed_by',
+      opr: 'rel_o_m',
+      value: 1,
+    },
+  ],
+  created: [
+    {
+      col: 'owners',
+      opr: 'rel_m_m',
+      value: 1,
+    },
+  ],
+};
+
+export default function ActivityTable({ user, activityFilter }: MapProps) {
+  const [active, setActiveState] = useState([]);
+  const recent = `/superset/recent_activity/${user.userId}/?limit=10`;
+  const setData = (endpoint: string) => {
+    SupersetClient.get(endpoint)
+      .then(({ json }) => {
+        setActiveState(json);
+      })
+      .catch(e => {
+        createErrorHandler(err =>
+          addDangerToast(t('There was an issue fetching your resource')),
+        );
+      });
+  };
+
+  const setBatchData = (q: string) => {
+    createBatchMethod(q).then((res: Array<object>) => {
+      setActiveState(res);
+    });
+  };
+
+  const getData = () => {
+    const queryParams = rison.encode({
+      order_column: 'changed_on_delta_humanized',
+      order_direction: 'desc',
+      page: 0,
+      page_size: 0,
+      filters: activityFilter !== 'Created' ? filters.edited : filters.created,
+    });
+    if (activityFilter === 'Viewed') {
+      setData({ endpoint: recent });
+    }
+    if (activityFilter === 'Edited') {
+      setBatchData(queryParams);
+    }
+    if (activityFilter === 'Created') {
+      setBatchData(queryParams);
+    }
+  };
+
+  useEffect(() => {
+    getData();
+  }, [activityFilter]);
+
+  const renderActivity = () => {
+    return active.map((e: MapProps) => (
+      <a href={e.item_url}>
+        <Cards>
+          <div>{activityFilter === 'Viewed' ? e.item_title : e.slice_name}</div>
+          <div>
+            {moment
+              .utc(activityFilter === 'Viewd' ? e.time : e.changed_on_utc)
+              .fromNow()}
+          </div>
+        </Cards>
+      </a>
+    ));
+  };
+
+  return <> {renderActivity()} </>;
+}
diff --git a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
index 39c5503..ca3f584 100644
--- a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
@@ -17,24 +17,14 @@
  * under the License.
  */
 import React, { useEffect } from 'react';
-import { t } from '@superset-ui/core';
-import { debounce } from 'lodash';
-import ListView, { FetchDataConfig } from 'src/components/ListView';
+import { t, styled } from '@superset-ui/core';
 import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import { User } from 'src/types/bootstrapTypes';
-import ListViewCard from 'src/components/ListViewCard';
-import { Dropdown, Menu } from 'src/common/components';
-import Icon from 'src/components/Icon';
-import Label from 'src/components/Label';
 import Owner from 'src/types/Owner';
-import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
 import DashboardCard from '../dashboard/DashboardCard';
-const PAGE_SIZE = 3;
 
-// const canEdit = hasPerm('can_edit');
-// const canDelete = hasPerm('can_delete');
-// const canExport = hasPerm('can_mulexport');
+const PAGE_SIZE = 3;
 
 interface DashboardTableProps {
   addDangerToast: (message: string) => void;
@@ -58,22 +48,12 @@ interface Dashboard {
   loading: boolean;
 }
 
-interface DashboardTableState {
-  dashboards: Dashboard[];
-  dashboard_count: number;
-  loading: boolean;
-}
-
 export interface FilterValue {
   col: string;
   operator: string;
   value: string | boolean | number | null | undefined;
 }
 
-export interface FetchDataConfig {
-  filters: FilterValue[];
-}
-
 function DashboardTable({
   dashboardFilter,
   user,
@@ -91,7 +71,6 @@ function DashboardTable({
     t('dashboard'),
     addDangerToast,
   );
-  console.log('dashboardFilter', dashboardFilter);
   const getFilters = () => {
     const filters = [];
 
@@ -133,7 +112,7 @@ function DashboardTable({
   }, [dashboardFilter]);
 
   return (
-    <div>
+    <>
       {dashboards.map(e => (
         <DashboardCard
           {...{
@@ -146,7 +125,7 @@ function DashboardTable({
           }}
         />
       ))}
-    </div>
+    </>
   );
 }
 
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
index 7f261cc..116cf91 100644
--- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -18,12 +18,18 @@
  */
 import React from 'react';
 import { t, SupersetClient } from '@superset-ui/core';
-import { debounce } from 'lodash';
-import withToasts from 'src/messageToasts/enhancers/withToasts';
+// import withToasts from 'src/messageToasts/enhancers/withToasts';
+import { Dropdown } from 'src/common/components';
 import ListViewCard from 'src/components/ListViewCard';
+// import FaveStar from 'src/components/FaveStar';
+import Icon from 'src/components/Icon';
+
+interface StateProps {
+  queries: Array<object>;
+}
 
 class SavedQueries extends React.PureComponent {
-  constructor(props: Readonly<{}>) {
+  constructor(props: StateProps) {
     super(props);
     this.state = {
       queries: [],
@@ -32,28 +38,20 @@ class SavedQueries extends React.PureComponent {
   componentDidMount() {
     this.fetchData();
   }
+  // eslint-disable-next-line consistent-return
   fetchData = async () => {
     try {
-      const ids = [];
       const { json } = await SupersetClient.get({
         endpoint: `/api/v1/query/`,
       });
-      /*(json.ids.forEach(id => {
-        const { json } = SupersetClient.get({
-          endpoint: `/api/v1/query/${id}`,
-        });
-        console.log('data', json);
-      });*/
-      console.log('json.result', json);
       this.setState({ queries: json.result });
     } catch (e) {
       return console.log(e);
     }
   };
   render() {
-    console.log('q', this.state.queries)
     return (
-      <div>
+      <>
         {this.state.queries.map(q => (
           <ListViewCard
             title={q.database.database_name}
@@ -61,9 +59,16 @@ class SavedQueries extends React.PureComponent {
             loading={false}
             description={t('Last run ', q.end_time)}
             showImg={false}
+            actions={
+              <ListViewCard.Actions>
+                <Dropdown>
+                  <Icon name="more" />
+                </Dropdown>
+              </ListViewCard.Actions>
+            }
           />
         ))}
-      </div>
+      </>
     );
   }
 }
diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index a610d43..5b05075 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -19,8 +19,8 @@
 import React, { useState } from 'react';
 import { FormControl } from 'react-bootstrap';
 import SubMenu from 'src/components/Menu/SubMenu';
+import { styled, t } from '@superset-ui/core';
 import { Collapse } from 'src/common/components';
-import { t } from '@superset-ui/core';
 import { useQueryParam, StringParam, QueryParamConfig } from 'use-query-params';
 import { User } from 'src/types/bootstrapTypes';
 import RecentActivity from 'src/profile/components/RecentActivity';
@@ -34,6 +34,24 @@ interface WelcomeProps {
   user: User;
 }
 
+const ActivityContainer = styled.div`
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(300px, max-content));
+  grid-gap: ${({ theme }) => theme.gridUnit * 8}px;
+  justify-content: center;
+  padding: ${({ theme }) => theme.gridUnit * 2}px
+    ${({ theme }) => theme.gridUnit * 4}px;
+`;
+
+export const CardContainer = styled.div`
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(459px, max-content));
+  grid-gap: ${({ theme }) => theme.gridUnit * 8}px;
+  justify-content: center;
+  padding: ${({ theme }) => theme.gridUnit * 2}px
+    ${({ theme }) => theme.gridUnit * 4}px;
+`;
+
 function useSyncQueryState(
   queryParam: string,
   queryParamType: QueryParamConfig<
@@ -75,7 +93,7 @@ export default function Welcome({ user }: WelcomeProps) {
     <Collapse defaultActiveKey={['1']}>
       <Panel header={t('Recents')} key="1">
         <SubMenu
-          activeChild="Viewed"
+          activeChild={activtyFilter}
           name=""
           // eslint-disable-next-line react/no-children-prop
           children={[
@@ -96,7 +114,9 @@ export default function Welcome({ user }: WelcomeProps) {
             },
           ]}
         />
-        <RecentActivity user={user} />
+        <ActivityContainer>
+          <ActivityTable user={user} activityFilter={activtyFilter} />
+        </ActivityContainer>
       </Panel>
 
       <Panel header={t('Dashboards')} key="2">
@@ -125,11 +145,13 @@ export default function Welcome({ user }: WelcomeProps) {
           // @ts-ignore React bootstrap types aren't quite right here
           onChange={e => setSearchQuery(e.currentTarget.value)}
         />
-        <DashboardTable
-          search={searchQuery}
-          dashboardFilter={dashboardFilter}
-          user={user}
-        />
+        <CardContainer>
+          <DashboardTable
+            search={searchQuery}
+            dashboardFilter={dashboardFilter}
+            user={user}
+          />
+        </CardContainer>
       </Panel>
 
       <Panel header={t('Saved Queries')} key="3">
@@ -150,7 +172,9 @@ export default function Welcome({ user }: WelcomeProps) {
             },
           ]}
         />
-        <SavedQueries />
+        <CardContainer>
+          <SavedQueries />
+        </CardContainer>
       </Panel>
       <Panel header={t('Charts')} key="4">
         <SubMenu
diff --git a/superset/views/api.py b/superset/views/api.py
index 19686a6..a5090b3 100644
--- a/superset/views/api.py
+++ b/superset/views/api.py
@@ -42,11 +42,9 @@ class Api(BaseSupersetView):
 
         raises SupersetSecurityException: If the user cannot access the resource
         """
-        print("hello")
         query_context = QueryContext(**json.loads(request.form["query_context"]))
         query_context.raise_for_access()
         payload_json = query_context.get_payload()
-        print("playload", payload_json)
         return json.dumps(
             payload_json, default=utils.json_int_dttm_ser, ignore_nan=True
         )


[incubator-superset] 24/33: add suggestions

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 659a49bbaef752ecbcf5e192eda05c2dace8761d
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Thu Oct 22 17:41:46 2020 -0700

    add suggestions
---
 superset-frontend/src/components/Menu/SubMenu.tsx  |   2 +-
 .../src/views/CRUD/chart/ChartCard.tsx             |   4 +-
 superset-frontend/src/views/CRUD/hooks.ts          |   5 -
 .../src/views/CRUD/welcome/ActivityTable.tsx       |  41 +++----
 .../src/views/CRUD/welcome/ChartTable.tsx          |  10 +-
 .../src/views/CRUD/welcome/DashboardTable.tsx      |   6 +-
 .../src/views/CRUD/welcome/EmptyState.tsx          | 128 ++++++++++-----------
 .../src/views/CRUD/welcome/SavedQueries.tsx        |  17 ++-
 .../src/views/CRUD/welcome/Welcome.tsx             |   8 +-
 9 files changed, 98 insertions(+), 123 deletions(-)

diff --git a/superset-frontend/src/components/Menu/SubMenu.tsx b/superset-frontend/src/components/Menu/SubMenu.tsx
index 67bceea..60cecb5 100644
--- a/superset-frontend/src/components/Menu/SubMenu.tsx
+++ b/superset-frontend/src/components/Menu/SubMenu.tsx
@@ -99,7 +99,7 @@ export interface ButtonProps {
 
 export interface SubMenuProps {
   buttons?: Array<ButtonProps>;
-  name: string;
+  name?: string;
   children?: MenuChild[];
   activeChild?: MenuChild['name'];
   /* If usesRouter is true, a react-router <Link> component will be used instead of href.
diff --git a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
index 05872a7..368b0f6 100644
--- a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
+++ b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
@@ -96,7 +96,7 @@ export default function ChartCard({
                 className="action-button"
                 onClick={confirmDelete}
               >
-                <ListViewCard.MenuIcon name="trash" /> Delete
+                <ListViewCard.MenuIcon name="trash" /> {t('Delete')}
               </div>
             )}
           </ConfirmStatusChange>
@@ -108,7 +108,7 @@ export default function ChartCard({
           tabIndex={0}
           onClick={() => openChartEditModal(chart)}
         >
-          <ListViewCard.MenuIcon name="edit-alt" /> Edit
+          <ListViewCard.MenuIcon name="edit-alt" /> {t('Edit')}
         </Menu.Item>
       )}
     </Menu>
diff --git a/superset-frontend/src/views/CRUD/hooks.ts b/superset-frontend/src/views/CRUD/hooks.ts
index 08e1262..1261a5e 100644
--- a/superset-frontend/src/views/CRUD/hooks.ts
+++ b/superset-frontend/src/views/CRUD/hooks.ts
@@ -35,11 +35,6 @@ interface ListViewResourceState<D extends object = any> {
   bulkSelectEnabled: boolean;
 }
 
-interface EditChartModal {
-  setCharts: () => void;
-  charts: Array<any>;
-}
-
 export function useListViewResource<D extends object = any>(
   resource: string,
   resourceLabel: string, // resourceLabel for translations
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index a198717..bb21c58 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -78,7 +78,7 @@ const ActivityContainer = styled.div`
     margin-left: 5px;
   }
   .ant-card-meta-title {
-    font-weight: 700;
+    font-weight: ${({ theme }) => theme.typography.weights.bold};
   }
 `;
 
@@ -105,7 +105,7 @@ export default function ActivityTable({ user }: ActivityProps) {
     if (e.url?.indexOf('explore') !== -1) {
       return 'nav-charts';
     }
-    if (e.item_url?.indexOf('explore') !== -1) {
+    if (e.url?.includes('explore') || e.item_url?.includes('explore')) {
       return 'nav-charts';
     }
     return '';
@@ -148,17 +148,17 @@ export default function ActivityTable({ user }: ActivityProps) {
 
   useEffect(() => {
     getBatchData(user.userId, recent)
-      .then(r => {
+      .then(res => {
         const data: any = {
-          Created: [...r.createdByChart, ...r.createdByDash],
-          Edited: [...r.editedChart, ...r.editedDash],
+          Created: [...res.createdByChart, ...res.createdByDash],
+          Edited: [...res.editedChart, ...res.editedDash],
         };
-        if (r.viewed) {
-          const filtered = reject(r.viewed, ['item_url', null]).map(r => r);
+        if (res.viewed) {
+          const filtered = reject(res.viewed, ['item_url', null]).map(r => r);
           data.Viewed = filtered;
           setActiveChild('Viewed');
         } else {
-          data.Examples = r.examples;
+          data.Examples = res.examples;
           setActiveChild('Examples');
         }
         setActivityData(data);
@@ -173,7 +173,7 @@ export default function ActivityTable({ user }: ActivityProps) {
   }, []);
 
   const renderActivity = () => {
-    return activityData[activeChild].map((e: MapProps, i: any) => (
+    return activityData[activeChild].map((e: MapProps, i: number) => (
       <ListViewCard
         key={`${i}`}
         isRecent
@@ -193,20 +193,17 @@ export default function ActivityTable({ user }: ActivityProps) {
   if (loading) return <>loading ...</>;
   return (
     <>
+      <SubMenu
+        activeChild={activeChild}
+        // eslint-disable-next-line react/no-children-prop
+        children={tabs}
+      />
       <>
-        <SubMenu
-          activeChild={activeChild}
-          name=""
-          // eslint-disable-next-line react/no-children-prop
-          children={tabs}
-        />
-        <>
-          {activityData[activeChild]?.length > 0 ? (
-            <ActivityContainer>{renderActivity()}</ActivityContainer>
-          ) : (
-            <EmptyState tableName="RECENTS" tab={activeChild} />
-          )}
-        </>
+        {activityData[activeChild]?.length > 0 ? (
+          <ActivityContainer>{renderActivity()}</ActivityContainer>
+        ) : (
+          <EmptyState tableName="RECENTS" tab={activeChild} />
+        )}
       </>
     </>
   );
diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
index c2784d1..a54d5a3 100644
--- a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
@@ -130,25 +130,23 @@ function ChartTable({
             ),
             buttonStyle: 'tertiary',
             onClick: () => {
-              // @ts-ignore
-              window.location = '/chart/add';
+              window.location.href = '/chart/add';
             },
           },
           {
             name: 'View All »',
             buttonStyle: 'link',
             onClick: () => {
-              // @ts-ignore
-              window.location = '/chart/list';
+              window.location.href = '/chart/list';
             },
           },
         ]}
       />
       {charts.length ? (
         <CardContainer>
-          {charts.map((e, i) => (
+          {charts.map(e => (
             <ChartCard
-              key={`${i}`}
+              key={`${e.id}`}
               openChartEditModal={openChartEditModal}
               loading={loading}
               chart={e}
diff --git a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
index ae3daeb..15bc8a3 100644
--- a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
@@ -146,16 +146,14 @@ function DashboardTable({
             ),
             buttonStyle: 'tertiary',
             onClick: () => {
-              // @ts-ignore
-              window.location = '/dashboard/new';
+              window.location.href = '/dashboard/new';
             },
           },
           {
             name: 'View All »',
             buttonStyle: 'link',
             onClick: () => {
-              // @ts-ignore
-              window.location = '/dashboard/list/';
+              window.location.href = '/dashboard/list/';
             },
           },
         ]}
diff --git a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
index 67bed3c..21a37ff 100644
--- a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
@@ -18,7 +18,8 @@
  */
 import React from 'react';
 import Button from 'src/components/Button';
-import { styled } from '@superset-ui/core';
+import { Empty } from 'src/common/components';
+import { t, styled } from '@superset-ui/core';
 import Icon from 'src/components/Icon';
 import { IconContainer } from '../utils';
 
@@ -27,29 +28,10 @@ interface EmptyStateProps {
   tab?: string;
 }
 
-const Container = styled.div`
-  display: flex;
-  flex-direction: column;
-  justify-content: center;
-  margin-top: 20px;
-  img {
-    width: 114px;
-    display: block;
-    margin: 0 auto;
-    margin-bottom: 15px;
-  }
-  div:nth-child(2) {
-    text-align: center;
-    margin-top: 15px;
-    color: ${({ theme }) => theme.colors.grayscale.dark1};
-    font-weight: 400;
-  }
-  button {
-    margin: 0 auto;
-    padding: 6px 27px;
-    margin-top: 10px;
+const ButtonContainer = styled.div`
+  Button {
     svg {
-      color: white;
+      color: ${({ theme }) => theme.colors.grayscale.light5};
     }
   }
 `;
@@ -72,64 +54,70 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
     SAVED_QUERIES: 'empty-queries.png',
   };
   const mine = (
-    <div>
-      <div>{`No ${
-        tableName === 'SAVED_QUERIES'
-          ? 'saved queries'
-          : tableName.toLowerCase()
-      } yet`}</div>
-      <Button
-        buttonStyle="primary"
-        onClick={() => {
-          window.location = mineRedirects[tableName];
-        }}
-      >
-        <IconContainer>
-          <Icon name="plus-small" />{' '}
-          {tableName === 'SAVED_QUERIES'
-            ? 'SQL QUERY'
-            : tableName
-                .split('')
-                .slice(0, tableName.length - 1)
-                .join('')}{' '}
-        </IconContainer>
-      </Button>
-    </div>
+    <div>{`No ${
+      tableName === 'SAVED_QUERIES'
+        ? t('saved queries')
+        : t(`${tableName.toLowerCase()}`)
+    } yet`}</div>
   );
   const span = (
     <div className="no-recents">
-      Recently {tab?.toLowerCase()} charts, dashboards, and saved queries will
-      appear here
+      {t(`Recently ${tab?.toLowerCase()} charts, dashboards, and saved queries will
+      appear here`)}
     </div>
   );
   // Mine and Recent Activity(all tabs) tab empty state
   if (tab === 'Mine' || tableName === 'RECENTS') {
     return (
-      <Container>
-        <img
-          src={`/static/assets/images/${tableIcon[tableName]}`}
-          alt={`${tableName}`}
-        />
-        {tableName === 'RECENTS' ? span : mine}
-      </Container>
+      <Empty
+        image={`/static/assets/images/${tableIcon[tableName]}`}
+        description={tableName === 'RECENTS' ? span : mine}
+      >
+        {tableName !== 'RECENTS' && (
+          <ButtonContainer>
+            <Button
+              buttonStyle="primary"
+              onClick={() => {
+                window.location = mineRedirects[tableName];
+              }}
+            >
+              <IconContainer>
+                <Icon name="plus-small" />{' '}
+                {tableName === 'SAVED_QUERIES'
+                  ? t('SQL QUERY')
+                  : t(`${tableName
+                      .split('')
+                      .slice(0, tableName.length - 1)
+                      .join('')}
+                    `)}
+              </IconContainer>
+            </Button>
+          </ButtonContainer>
+        )}
+      </Empty>
     );
   }
   // Favorite tab empty state
   return (
-    <Container>
-      <img src="/static/assets/images/star-circle.png" alt="star.png" />
-      <div>
-        <div className="no-favorites">You don't have any favorites yet!</div>
-        <Button
-          buttonStyle="primary"
-          onClick={() => {
-            window.location = favRedirects[tableName];
-          }}
-        >
-          SEE ALL{' '}
-          {tableName === 'SAVED_QUERIES' ? 'SQL LAB QUERIES' : tableName}
-        </Button>
-      </div>
-    </Container>
+    <Empty
+      image="/static/assets/images/star-circle.png"
+      description={
+        <div className="no-favorites">
+          {t("You don't have any favorites yet!")}
+        </div>
+      }
+    >
+      <Button
+        buttonStyle="primary"
+        onClick={() => {
+          window.location = favRedirects[tableName];
+        }}
+      >
+        SEE ALL{' '}
+        {tableName === 'SAVED_QUERIES'
+          ? t('SQL LAB QUERIES')
+          : t(`${tableName}`)}
+      </Button>
+    </Empty>
   );
 }
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
index d2aae27..43646d4 100644
--- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -124,10 +124,10 @@ const SavedQueries = ({
         <Menu.Item
           onClick={() => {
             // @ts-ignore
-            window.location = `/superset/sqllab?savedQueryId=${query.id}`;
+            window.location.href = `/superset/sqllab?savedQueryId=${query.id}`;
           }}
         >
-          Edit
+          {t('Edit')}
         </Menu.Item>
       )}
       <Menu.Item
@@ -136,7 +136,7 @@ const SavedQueries = ({
             copyQueryLink(query.id, addDangerToast, addSuccessToast);
         }}
       >
-        Share
+        {t('Share')}
       </Menu.Item>
       {canDelete && (
         <Menu.Item
@@ -145,7 +145,7 @@ const SavedQueries = ({
             setCurrentlyEdited(query);
           }}
         >
-          Delete
+          {t('Delete')}
         </Menu.Item>
       )}
     </Menu>
@@ -194,8 +194,7 @@ const SavedQueries = ({
             ),
             buttonStyle: 'tertiary',
             onClick: () => {
-              // @ts-ignore
-              window.location = '/superset/sqllab';
+              window.location.href = '/superset/sqllab';
             },
           },
           {
@@ -203,16 +202,16 @@ const SavedQueries = ({
             buttonStyle: 'link',
             onClick: () => {
               // @ts-ignore
-              window.location = '/savedqueryview/list';
+              window.location.href = '/savedqueryview/list';
             },
           },
         ]}
       />
       {queries.length > 0 ? (
         <CardContainer>
-          {queries.map((q, i) => (
+          {queries.map(q => (
             <ListViewCard
-              key={`${i}`}
+              key={`${q.id}`}
               imgFallbackURL=""
               imgURL=""
               url={`/superset/sqllab?savedQueryId=${q.id}`}
diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index 4719468..effe8cb 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -41,7 +41,7 @@ const WelcomeContainer = styled.div`
       content: '';
       display: block;
       border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
-      margin: 0px 26px;
+      margin: 0px ${({ theme }) => theme.gridUnit * 6}px;
       position: relative;
       ${[mq[1]]} {
         margin-top: 5px;
@@ -52,7 +52,7 @@ const WelcomeContainer = styled.div`
       & > li:nth-child(1),
       & > li:nth-child(2),
       & > li:nth-child(3) {
-        margin-top: 8px;
+        margin-top: ${({ theme }) => theme.gridUnit * 2}px;
       }
     }
     button {
@@ -67,8 +67,8 @@ const WelcomeContainer = styled.div`
     border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
   }
   .ant-collapse-header {
-    font-weight: 500;
-    font-size: 16px;
+    font-weight: ${({ theme }) => theme.typography.weights.normal};
+    font-size: ${({ theme }) => theme.gridUnit * 4}px;
   }
 `;
 


[incubator-superset] 28/33: update from comments

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit f94502beaee46e03386f1988eca8a54ddea08b5d
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Sun Oct 25 20:42:42 2020 -0700

    update from comments
---
 .../spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx |  3 ---
 .../javascripts/views/CRUD/welcome/SavedQueries_spec.tsx    |  4 +---
 superset-frontend/src/components/ListViewCard/index.tsx     |  8 ++++----
 superset-frontend/src/components/Menu/SubMenu.tsx           |  2 --
 superset-frontend/src/views/CRUD/chart/ChartCard.tsx        | 11 +++++------
 .../src/views/CRUD/dashboard/DashboardCard.tsx              | 11 +++++------
 superset-frontend/src/views/CRUD/utils.tsx                  |  5 ++++-
 superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx  | 13 ++++---------
 8 files changed, 23 insertions(+), 34 deletions(-)

diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx
index 7eaa234..96ec1ec 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx
@@ -75,9 +75,6 @@ describe('EmptyState', () => {
                 : variant.tableName.toLowerCase()
             } yet`,
       );
-      console.log('wrapper', wrapper.debug());
-      console.log('wrapper', wrapper.find('ant-empty-image').children().debug());
-      //expect(wrapper.find('ant-empty-image').children()).toHaveLength(1);
       expect(wrapper.find('button')).toHaveLength(1);
     });
   });
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
index 015e4eb..2670482 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/SavedQueries_spec.tsx
@@ -90,12 +90,11 @@ describe('SavedQueries', () => {
   it('it renders a submenu with clickable tables and buttons', async () => {
     expect(wrapper.find(SubMenu)).toExist();
     expect(wrapper.find('MenuItem')).toHaveLength(2);
-    console.log('button', wrapper.find('button').length);
+    expect(wrapper.find('button')).toHaveLength(2);
     act(() => {
       wrapper.find('MenuItem').at(1).simulate('click');
     });
 
-    console.log('menu item', wrapper.find('MenuItem').at(1).debug());
     await waitForComponentToPaint(wrapper);
     expect(fetchMock.calls(/saved_query\/\?q/)).toHaveLength(1);
   });
@@ -103,6 +102,5 @@ describe('SavedQueries', () => {
   it('fetches queries favorites and renders listviewcard cards', () => {
     expect(fetchMock.calls(/saved_query\/\?q/)).toHaveLength(1);
     expect(wrapper.find('ListViewCard')).toExist();
-    console.log('wrapper', wrapper);
   });
 });
diff --git a/superset-frontend/src/components/ListViewCard/index.tsx b/superset-frontend/src/components/ListViewCard/index.tsx
index a974889..02f60f6 100644
--- a/superset-frontend/src/components/ListViewCard/index.tsx
+++ b/superset-frontend/src/components/ListViewCard/index.tsx
@@ -145,8 +145,8 @@ const QueryData = styled.div`
   justify-content: flex-start;
   border-bottom: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
   .title {
-    font-weight: 500;
-    color: #b2b2b2;
+    font-weight: ${({ theme }) => theme.typography.weights.normal};
+    color: ${({ theme }) => theme.colors.grayscale.light2};
   }
   .holder {
     margin: ${({ theme }) => theme.gridUnit * 2}px;
@@ -157,9 +157,9 @@ const paragraphConfig = { rows: 1, width: 150 };
 interface CardProps {
   title: React.ReactNode;
   url?: string;
-  imgURL: string;
+  imgURL?: string;
   tables?: string | number;
-  imgFallbackURL: string;
+  imgFallbackURL?: string;
   imgPosition?: BackgroundPosition;
   description: string;
   loading: boolean;
diff --git a/superset-frontend/src/components/Menu/SubMenu.tsx b/superset-frontend/src/components/Menu/SubMenu.tsx
index 60cecb5..e9c7976 100644
--- a/superset-frontend/src/components/Menu/SubMenu.tsx
+++ b/superset-frontend/src/components/Menu/SubMenu.tsx
@@ -74,8 +74,6 @@ const StyledHeader = styled.header`
   }
 `;
 
-type MenuClickHandler = React.MouseEventHandler<MenuItem>;
-
 type MenuChild = {
   label: string;
   name: string;
diff --git a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
index 368b0f6..9e3b696 100644
--- a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
+++ b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
@@ -54,12 +54,11 @@ export default function ChartCard({
 }: ChartCardProps) {
   const canEdit = hasPerm('can_edit');
   const canDelete = hasPerm('can_delete');
-  const [
-    favoriteStatusRef,
-    fetchFaveStar,
-    saveFaveStar,
-    favoriteStatus,
-  ] = useFavoriteStatus({}, FAVESTAR_BASE_URL, addDangerToast);
+  const [, fetchFaveStar, saveFaveStar, favoriteStatus] = useFavoriteStatus(
+    {},
+    FAVESTAR_BASE_URL,
+    addDangerToast,
+  );
 
   function handleChartDelete({ id, slice_name: sliceName }: Chart) {
     SupersetClient.delete({
diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
index 2469d64..6058563 100644
--- a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
+++ b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
@@ -27,12 +27,11 @@ function DashboardCard({
   const canEdit = hasPerm('can_edit');
   const canDelete = hasPerm('can_delete');
   const canExport = hasPerm('can_mulexport');
-  const [
-    favoriteStatusRef,
-    fetchFaveStar,
-    saveFaveStar,
-    favoriteStatus,
-  ] = useFavoriteStatus({}, FAVESTAR_BASE_URL, addDangerToast);
+  const [, fetchFaveStar, saveFaveStar, favoriteStatus] = useFavoriteStatus(
+    {},
+    FAVESTAR_BASE_URL,
+    addDangerToast,
+  );
 
   function handleDashboardDelete({
     id,
diff --git a/superset-frontend/src/views/CRUD/utils.tsx b/superset-frontend/src/views/CRUD/utils.tsx
index 449d57f..55ae299 100644
--- a/superset-frontend/src/views/CRUD/utils.tsx
+++ b/superset-frontend/src/views/CRUD/utils.tsx
@@ -54,7 +54,10 @@ const createFetchResourceMethod = (method: string) => (
   return [];
 };
 
-export const getBatchData = (userId: string | number, recent: string) => {
+export const getRecentAcitivtyObjs = (
+  userId: string | number,
+  recent: string,
+) => {
   const getParams = (filters?: Array<any>) => {
     const params = {
       order_column: 'changed_on_delta_humanized',
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index ed963f6..5d80003 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -24,7 +24,7 @@ import ListViewCard from 'src/components/ListViewCard';
 import { addDangerToast } from 'src/messageToasts/actions';
 import SubMenu from 'src/components/Menu/SubMenu';
 import { reject } from 'lodash';
-import { getBatchData, mq } from '../utils';
+import { getRecentAcitivtyObjs, mq } from '../utils';
 import EmptyState from './EmptyState';
 
 interface MapProps {
@@ -102,9 +102,6 @@ export default function ActivityTable({ user }: ActivityProps) {
     if (e.url?.includes('dashboard')) {
       return 'nav-dashboard';
     }
-    if (e.url?.indexOf('explore') !== -1) {
-      return 'nav-charts';
-    }
     if (e.url?.includes('explore') || e.item_url?.includes('explore')) {
       return 'nav-charts';
     }
@@ -147,7 +144,7 @@ export default function ActivityTable({ user }: ActivityProps) {
   }
 
   useEffect(() => {
-    getBatchData(user.userId, recent)
+    getRecentAcitivtyObjs(user.userId, recent)
       .then(res => {
         const data: any = {
           Created: [...res.createdByChart, ...res.createdByDash],
@@ -173,13 +170,11 @@ export default function ActivityTable({ user }: ActivityProps) {
   }, []);
 
   const renderActivity = () => {
-    return activityData[activeChild].map((e: MapProps, i: number) => (
+    return activityData[activeChild].map((e: MapProps) => (
       <ListViewCard
-        key={`${i}`}
+        key={`${e.id}`}
         isRecent
         loading={loading}
-        imgURL=""
-        imgFallbackURL=""
         url={e.sql ? `/supserset/sqllab?queryId=${e.id}` : e.url}
         title={getFilterTitle(e)}
         description={`Last Edited: ${moment(e.changed_on_utc).format(


[incubator-superset] 09/33: Adding Expand Icon to Collapse component

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 40d2acf37f64481122f069c87ab0aa6216b3506b
Author: Evan Rusackas <ev...@preset.io>
AuthorDate: Fri Oct 2 16:48:55 2020 -0700

    Adding Expand Icon to Collapse component
---
 .../src/views/CRUD/welcome/Welcome.tsx             | 27 ++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index 1cd6db9..f4f1e7d 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -28,6 +28,7 @@ import ActivityTable from './ActivityTable';
 import ChartTable from './ChartTable';
 import SavedQueries from './SavedQueries';
 import DashboardTable from './DashboardTable';
+import Icon from 'src/components/Icon';
 
 const { Panel } = Collapse;
 
@@ -93,8 +94,20 @@ export default function Welcome({ user }: WelcomeProps) {
     '',
   );
   console.log('user', user);
+
+
+  function ExpandIcon(): React.ReactNode {
+    return (
+      <Icon name="caret-right" />
+    )
+  }
+
   return (
-    <Collapse defaultActiveKey={['1']}>
+    <Collapse
+      defaultActiveKey={['1']}
+      expandIcon={ExpandIcon}
+      ghost
+    >
       <Panel header={t('Recents')} key="1">
         <SubMenu
           activeChild={activityFilter}
@@ -159,7 +172,9 @@ export default function Welcome({ user }: WelcomeProps) {
             {
               name: 'View All',
               buttonStyle: 'link',
-              onClick: () => { window.location = '/dashboard/list/'}
+              onClick: () => {
+                window.location = '/dashboard/list/';
+              },
             },
           ]}
         />
@@ -212,7 +227,9 @@ export default function Welcome({ user }: WelcomeProps) {
             {
               name: 'View All',
               buttonStyle: 'link',
-              onClick: () => { window.location = 'superset/sqllab#search'}
+              onClick: () => {
+                window.location = 'superset/sqllab#search';
+              },
             },
           ]}
         />
@@ -252,7 +269,9 @@ export default function Welcome({ user }: WelcomeProps) {
             {
               name: 'View All',
               buttonStyle: 'link',
-              onClick: () => { window.location = '/chart/list'}
+              onClick: () => {
+                window.location = '/chart/list';
+              },
             },
           ]}
         />


[incubator-superset] 15/33: lil' ascii arrows

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 45b17fbd323e5edf7e8ec096fbde632060656d99
Author: Evan Rusackas <ev...@preset.io>
AuthorDate: Fri Oct 9 23:06:12 2020 -0700

    lil' ascii arrows
---
 superset-frontend/src/views/CRUD/welcome/Welcome.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index f6015b7..f0934e1 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -140,7 +140,7 @@ export default function Welcome({ user }: WelcomeProps) {
                 },
               },
               {
-                name: 'View All',
+                name: 'View All »',
                 buttonStyle: 'link',
                 onClick: () => {
                   // @ts-ignore
@@ -185,7 +185,7 @@ export default function Welcome({ user }: WelcomeProps) {
                 },
               },
               {
-                name: 'View All',
+                name: 'View All »',
                 buttonStyle: 'link',
                 onClick: () => {
                   // @ts-ignore
@@ -229,7 +229,7 @@ export default function Welcome({ user }: WelcomeProps) {
                 },
               },
               {
-                name: 'View All',
+                name: 'View All »',
                 buttonStyle: 'link',
                 onClick: () => {
                   // @ts-ignore


[incubator-superset] 01/33: step 1: broken stuff!

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit f476c02e070827485bb3982b199e960a88a80a68
Author: Evan Rusackas <ev...@preset.io>
AuthorDate: Thu Sep 10 13:57:48 2020 -0700

    step 1: broken stuff!
---
 superset-frontend/src/components/Menu/SubMenu.tsx  |  2 +
 .../src/views/CRUD/welcome/Welcome.tsx             | 53 ++++++++++++++++++++++
 2 files changed, 55 insertions(+)

diff --git a/superset-frontend/src/components/Menu/SubMenu.tsx b/superset-frontend/src/components/Menu/SubMenu.tsx
index e17ce7a..df7e501 100644
--- a/superset-frontend/src/components/Menu/SubMenu.tsx
+++ b/superset-frontend/src/components/Menu/SubMenu.tsx
@@ -61,6 +61,8 @@ const StyledHeader = styled.header`
   }
 `;
 
+type MenuClickHandler = React.MouseEventHandler<MenuItem>;
+
 type MenuChild = {
   label: string;
   name: string;
diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index 256fe3a..15d4928 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -33,6 +33,8 @@ import RecentActivity from 'src/profile/components/RecentActivity';
 import Favorites from 'src/profile/components/Favorites';
 import DashboardTable from './DashboardTable';
 
+const { Panel } = Collapse;
+
 interface WelcomeProps {
   user: User;
 }
@@ -56,6 +58,10 @@ function useSyncQueryState(
   return [state, setQueryStateAndState];
 }
 
+function ding(e: any) {
+  console.log('event',e);
+}
+
 export default function Welcome({ user }: WelcomeProps) {
   const [activeTab, setActiveTab] = useSyncQueryState(
     'activeTab',
@@ -82,6 +88,52 @@ export default function Welcome({ user }: WelcomeProps) {
   }, []);
 
   return (
+    <Collapse defaultActiveKey={['1']}>
+      <Panel header={t('Recents')} key="1">
+        <SubMenu
+          activeChild="Datasets"
+          name={''}
+          children={[
+            {
+              name: 'Viewed',
+              label: t('Viewed'),
+              onClick: () => ding('hi?'),
+            },
+            {
+              name: 'Edited',
+              label: t('Edited'),
+              onClick: ding,
+            },
+            {
+              name: 'Created',
+              label: t('Created'),
+              onClick: ding,
+            },
+          ]}
+        />
+        <RecentActivity user={user} />
+      </Panel>
+
+      <Panel header={t('Dashboards')} key="2">
+        <FormControl
+          type="text"
+          bsSize="sm"
+          placeholder="Search"
+          value={searchQuery}
+          // @ts-ignore React bootstrap types aren't quite right here
+          onChange={e => setSearchQuery(e.currentTarget.value)}
+        />
+        <DashboardTable search={searchQuery} />
+      </Panel>
+
+      <Panel header={t('Saved Queries')} key="3" >
+        Stuff here!
+      </Panel>
+      <Panel header={t('Charts')} key="4" >
+        Stuff here!
+      </Panel>
+    </Collapse>
+    /*
     <div className="container welcome">
       <Tabs
         activeKey={activeTab}
@@ -139,5 +191,6 @@ export default function Welcome({ user }: WelcomeProps) {
         </Tab>
       </Tabs>
     </div>
+    */
   );
 }


[incubator-superset] 30/33: fix rebase

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 629ad3702ee993ba01a8370e84d4bcff413fb549
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Sun Oct 25 21:58:52 2020 -0700

    fix rebase
---
 superset-frontend/src/views/CRUD/chart/ChartList.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/superset-frontend/src/views/CRUD/chart/ChartList.tsx b/superset-frontend/src/views/CRUD/chart/ChartList.tsx
index 8a0b8b7..da27780 100644
--- a/superset-frontend/src/views/CRUD/chart/ChartList.tsx
+++ b/superset-frontend/src/views/CRUD/chart/ChartList.tsx
@@ -38,7 +38,7 @@ import ListView, {
 } from 'src/components/ListView';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import PropertiesModal from 'src/explore/components/PropertiesModal';
-import Chart, { Slice } from 'src/types/Chart';
+import Chart from 'src/types/Chart';
 import TooltipWrapper from 'src/components/TooltipWrapper';
 import ChartCard from './ChartCard';
 


[incubator-superset] 14/33: fixing subnav button height shift

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 697171718706da524585ba421da5d14073382652
Author: Evan Rusackas <ev...@preset.io>
AuthorDate: Fri Oct 9 11:55:23 2020 -0700

    fixing subnav button height shift
---
 superset-frontend/src/components/Menu/SubMenu.tsx | 1 +
 1 file changed, 1 insertion(+)

diff --git a/superset-frontend/src/components/Menu/SubMenu.tsx b/superset-frontend/src/components/Menu/SubMenu.tsx
index 0144a20..67bceea 100644
--- a/superset-frontend/src/components/Menu/SubMenu.tsx
+++ b/superset-frontend/src/components/Menu/SubMenu.tsx
@@ -58,6 +58,7 @@ const StyledHeader = styled.header`
       background: ${({ theme }) => theme.colors.secondary.light4};
       border-bottom: none;
       border-radius: 4px;
+      margin-bottom: ${({ theme }) => theme.gridUnit * 2}px;
     }
   }
   .navbar-inverse {


[incubator-superset] 21/33: add changes

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 9655b48e826a602c17fad10780d0c196d5e5d68f
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Wed Oct 21 15:11:06 2020 -0700

    add changes
---
 .../javascripts/views/CRUD/welcome/ChartTable_spec.tsx    | 10 +++-------
 superset-frontend/src/views/CRUD/utils.tsx                |  2 +-
 .../src/views/CRUD/welcome/ActivityTable.tsx              | 15 ++++++++++++---
 superset-frontend/src/views/CRUD/welcome/EmptyState.tsx   |  6 ++++--
 superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx |  7 ++++---
 5 files changed, 24 insertions(+), 16 deletions(-)

diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx
index a631a78..7a39e9f 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx
@@ -24,6 +24,7 @@ import fetchMock from 'fetch-mock';
 import configureStore from 'redux-mock-store';
 import ChartTable from 'src/views/CRUD/welcome/ChartTable';
 import ChartCard from 'src/views/CRUD/chart/ChartCard';
+import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
 
 // store needed for withToasts(DashboardTable)
 const mockStore = configureStore([thunk]);
@@ -59,20 +60,15 @@ describe('DashboardTable', () => {
     context: { store },
   });
 
-  /*beforeAll(async () => {
+  beforeAll(async () => {
     await waitForComponentToPaint(wrapper);
-  });*/
+  });
 
   it('it renders', () => {
     expect(wrapper.find(ChartTable)).toExist();
   });
   it('fetches chart favorites and renders chart cards ', () => {
-      console.log('mock call', fetchMock.calls(/chart\/\?q/))
     expect(fetchMock.calls(chartsEndpoint)).toHaveLength(1);
-    console.log('wrapper', wrapper.dive());
-    // there's a delay between response and updating state, so manually set it
-    // rather than adding a timeout which could introduce flakiness
-    // wrapper.setState({ dashboards: mockDashboards });
     expect(wrapper.find(ChartCard)).toExist();
   });
 });
diff --git a/superset-frontend/src/views/CRUD/utils.tsx b/superset-frontend/src/views/CRUD/utils.tsx
index cf4c9ec..449d57f 100644
--- a/superset-frontend/src/views/CRUD/utils.tsx
+++ b/superset-frontend/src/views/CRUD/utils.tsx
@@ -164,7 +164,7 @@ export const CardContainer = styled.div`
   }
 
   ${[mq[1]]} {
-    grid-template-columns: repeat(auto-fit, minmax(48%, max-content));
+    grid-template-columns: repeat(auto-fit, minmax(50%, max-content));
   }
   grid-gap: ${({ theme }) => theme.gridUnit * 8}px;
   justify-content: left;
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index 31dce1c..a198717 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -24,7 +24,7 @@ import ListViewCard from 'src/components/ListViewCard';
 import { addDangerToast } from 'src/messageToasts/actions';
 import SubMenu from 'src/components/Menu/SubMenu';
 import { reject } from 'lodash';
-import { getBatchData } from '../utils';
+import { getBatchData, mq } from '../utils';
 import EmptyState from './EmptyState';
 
 interface MapProps {
@@ -60,12 +60,22 @@ const ActivityContainer = styled.div`
   margin-top: -15px;
   display: grid;
   grid-template-columns: repeat(auto-fit, minmax(31%, max-content));
+  ${[mq[3]]} {
+    grid-template-columns: repeat(auto-fit, minmax(31%, max-content));
+  }
+  ${[mq[2]]} {
+    grid-template-columns: repeat(auto-fit, minmax(42%, max-content));
+  }
+  ${[mq[1]]} {
+    grid-template-columns: repeat(auto-fit, minmax(63%, max-content));
+  }
   grid-gap: ${({ theme }) => theme.gridUnit * 8}px;
   justify-content: left;
   padding: ${({ theme }) => theme.gridUnit * 2}px
     ${({ theme }) => theme.gridUnit * 4}px;
   .ant-card-meta-avatar {
-    margin-top: 5px;
+    margin-top: 10px;
+    margin-left: 5px;
   }
   .ant-card-meta-title {
     font-weight: 700;
@@ -181,7 +191,6 @@ export default function ActivityTable({ user }: ActivityProps) {
     ));
   };
   if (loading) return <>loading ...</>;
-  console.log('activity', activeChild)
   return (
     <>
       <>
diff --git a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
index 00aff9b..67bed3c 100644
--- a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
@@ -31,10 +31,12 @@ const Container = styled.div`
   display: flex;
   flex-direction: column;
   justify-content: center;
+  margin-top: 20px;
   img {
     width: 114px;
     display: block;
     margin: 0 auto;
+    margin-bottom: 15px;
   }
   div:nth-child(2) {
     text-align: center;
@@ -100,7 +102,7 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
       appear here
     </div>
   );
-
+  // Mine and Recent Activity(all tabs) tab empty state
   if (tab === 'Mine' || tableName === 'RECENTS') {
     return (
       <Container>
@@ -112,7 +114,7 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) {
       </Container>
     );
   }
-
+  // Favorite tab empty state
   return (
     <Container>
       <img src="/static/assets/images/star-circle.png" alt="star.png" />
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
index 82de6a3..d2aae27 100644
--- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -150,7 +150,6 @@ const SavedQueries = ({
       )}
     </Menu>
   );
-
   return (
     <>
       {queryDeleteModal && (
@@ -211,13 +210,15 @@ const SavedQueries = ({
       />
       {queries.length > 0 ? (
         <CardContainer>
-          {queries.map(q => (
+          {queries.map((q, i) => (
             <ListViewCard
+              key={`${i}`}
               imgFallbackURL=""
               imgURL=""
+              url={`/superset/sqllab?savedQueryId=${q.id}`}
               title={q.label}
               rows={q.rows}
-              tableName={q?.sql_tables && q.sql_tables[0].table}
+              tableName={q?.sql_tables && q.sql_tables[0]?.table}
               tables={q?.sql_tables?.length}
               loading={loading}
               description={t('Last run ', q.end_time)}


[incubator-superset] 05/33: can now filter dashboards/charts for "Edited" tabs (filter by changed_by o_m)

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit b38ddfb3c322795ac98864e27200db6ecd56aca9
Author: Evan Rusackas <ev...@preset.io>
AuthorDate: Tue Sep 22 16:05:59 2020 -0700

    can now filter dashboards/charts for "Edited" tabs (filter by changed_by o_m)
---
 superset/charts/api.py     | 1 +
 superset/dashboards/api.py | 1 +
 2 files changed, 2 insertions(+)

diff --git a/superset/charts/api.py b/superset/charts/api.py
index 3ef27d0..6ebe3cb 100644
--- a/superset/charts/api.py
+++ b/superset/charts/api.py
@@ -144,6 +144,7 @@ class ChartRestApi(BaseSupersetModelRestApi):
     ]
     search_columns = [
         "created_by",
+        "changed_by",
         "datasource_id",
         "datasource_name",
         "datasource_type",
diff --git a/superset/dashboards/api.py b/superset/dashboards/api.py
index 8f65f35..0e93159 100644
--- a/superset/dashboards/api.py
+++ b/superset/dashboards/api.py
@@ -157,6 +157,7 @@ class DashboardRestApi(BaseSupersetModelRestApi):
         "owners",
         "published",
         "slug",
+        "changed_by"
     )
     search_filters = {
         "dashboard_title": [DashboardTitleOrSlugFilter],


[incubator-superset] 29/33: more updates..

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 23f5424e88371339d35c5a2d138f781abd8fa6ff
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Sun Oct 25 21:38:07 2020 -0700

    more updates..
---
 superset-frontend/src/components/Menu/SubMenu.tsx  | 24 +++++++++++-----------
 .../src/views/CRUD/dashboard/DashboardCard.tsx     | 18 ++++++++++++++++
 .../src/views/CRUD/welcome/ActivityTable.tsx       |  2 +-
 .../src/views/CRUD/welcome/ChartTable.tsx          |  3 +--
 .../src/views/CRUD/welcome/DashboardTable.tsx      |  4 +---
 .../src/views/CRUD/welcome/SavedQueries.tsx        |  4 +---
 superset/dashboards/api.py                         |  2 +-
 7 files changed, 35 insertions(+), 22 deletions(-)

diff --git a/superset-frontend/src/components/Menu/SubMenu.tsx b/superset-frontend/src/components/Menu/SubMenu.tsx
index e9c7976..f58c2bd 100644
--- a/superset-frontend/src/components/Menu/SubMenu.tsx
+++ b/superset-frontend/src/components/Menu/SubMenu.tsx
@@ -98,7 +98,7 @@ export interface ButtonProps {
 export interface SubMenuProps {
   buttons?: Array<ButtonProps>;
   name?: string;
-  children?: MenuChild[];
+  tabs?: MenuChild[];
   activeChild?: MenuChild['name'];
   /* If usesRouter is true, a react-router <Link> component will be used instead of href.
    *  ONLY set usesRouter to true if SubMenu is wrapped in a react-router <Router>;
@@ -122,16 +122,16 @@ const SubMenu: React.FunctionComponent<SubMenuProps> = props => {
           <Navbar.Brand>{props.name}</Navbar.Brand>
         </Navbar.Header>
         <Nav>
-          {props.children &&
-            props.children.map(child => {
-              if ((props.usesRouter || hasHistory) && !!child.usesRouter) {
+          {props.tabs &&
+            props.tabs.map(tab => {
+              if ((props.usesRouter || hasHistory) && !!tab.usesRouter) {
                 return (
                   <li
-                    className={child.name === props.activeChild ? 'active' : ''}
-                    key={`${child.label}`}
+                    className={tab.name === props.activeChild ? 'active' : ''}
+                    key={`${tab.label}`}
                   >
                     <div>
-                      <Link to={child.url}>{child.label}</Link>
+                      <Link to={tab.url}>{tab.label}</Link>
                     </div>
                   </li>
                 );
@@ -140,12 +140,12 @@ const SubMenu: React.FunctionComponent<SubMenuProps> = props => {
               return (
                 <MenuItem
                   className="no-router"
-                  active={child.name === props.activeChild}
-                  key={`${child.label}`}
-                  href={child.url}
-                  onClick={child.onClick}
+                  active={tab.name === props.activeChild}
+                  key={`${tab.label}`}
+                  href={tab.url}
+                  onClick={tab.onClick}
                 >
-                  {child.label}
+                  {tab.label}
                 </MenuItem>
               );
             })}
diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
index 6058563..135246e 100644
--- a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
+++ b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
@@ -1,3 +1,21 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
 import React from 'react';
 import { SupersetClient, t } from '@superset-ui/core';
 import rison from 'rison';
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index 5d80003..56e5d70 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -191,7 +191,7 @@ export default function ActivityTable({ user }: ActivityProps) {
       <SubMenu
         activeChild={activeChild}
         // eslint-disable-next-line react/no-children-prop
-        children={tabs}
+        tabs={tabs}
       />
       <>
         {activityData[activeChild]?.length > 0 ? (
diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
index e12e204..1979a18 100644
--- a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
@@ -107,9 +107,8 @@ function ChartTable({
 
       <SubMenu
         activeChild={chartFilter}
-        name=""
         // eslint-disable-next-line react/no-children-prop
-        children={[
+        tabs={[
           {
             name: 'Favorite',
             label: t('Favorite'),
diff --git a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
index 15bc8a3..84ec1c6 100644
--- a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
@@ -123,9 +123,7 @@ function DashboardTable({
     <>
       <SubMenu
         activeChild={dashboardFilter}
-        name=""
-        // eslint-disable-next-line react/no-children-prop
-        children={[
+        tabs={[
           {
             name: 'Favorite',
             label: t('Favorite'),
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
index 563ac14..be35192 100644
--- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -170,9 +170,7 @@ const SavedQueries = ({
       )}
       <SubMenu
         activeChild={queryFilter}
-        name=""
-        // eslint-disable-next-line react/no-children-prop
-        children={[
+        tabs={[
           {
             name: 'Favorite',
             label: t('Favorite'),
diff --git a/superset/dashboards/api.py b/superset/dashboards/api.py
index 0e93159..cba494a 100644
--- a/superset/dashboards/api.py
+++ b/superset/dashboards/api.py
@@ -157,7 +157,7 @@ class DashboardRestApi(BaseSupersetModelRestApi):
         "owners",
         "published",
         "slug",
-        "changed_by"
+        "changed_by",
     )
     search_filters = {
         "dashboard_title": [DashboardTitleOrSlugFilter],


[incubator-superset] 07/33: update recent card

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 79353318f28e3f8571ae788b3e8ca45c5314d818
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Fri Oct 2 16:22:28 2020 -0700

    update recent card
---
 .../src/components/ListViewCard/index.tsx          | 26 ++++--
 superset-frontend/src/components/Menu/SubMenu.tsx  |  4 +-
 .../src/views/CRUD/chart/ChartList.tsx             | 74 ++---------------
 superset-frontend/src/views/CRUD/utils.tsx         | 16 ++++
 .../src/views/CRUD/welcome/ActivityTable.tsx       | 36 ++++++--
 .../src/views/CRUD/welcome/ChartTable.tsx          | 10 ++-
 .../src/views/CRUD/welcome/DashboardTable.tsx      | 10 ++-
 .../src/views/CRUD/welcome/SavedQueries.tsx        | 19 +++--
 .../src/views/CRUD/welcome/Welcome.tsx             | 97 ++++++++++++++++++----
 9 files changed, 186 insertions(+), 106 deletions(-)

diff --git a/superset-frontend/src/components/ListViewCard/index.tsx b/superset-frontend/src/components/ListViewCard/index.tsx
index fc62f7b..5e06e79 100644
--- a/superset-frontend/src/components/ListViewCard/index.tsx
+++ b/superset-frontend/src/components/ListViewCard/index.tsx
@@ -166,16 +166,19 @@ function ListViewCard({
   coverLeft,
   coverRight,
   actions,
+  avatar,
   loading,
   imgPosition = 'top',
   showImg = true,
   rows,
+  isRecent,
 }: CardProps) {
   return (
     <StyledCard
       data-test="styled-card"
       cover={
-        showImg ? (
+        !isRecent &&
+        (showImg ? (
           <Cover>
             <a href={url}>
               <div className="gradient-container">
@@ -197,11 +200,21 @@ function ListViewCard({
             </CoverFooter>
           </Cover>
         ) : (
-          <>
-            <div>Rows</div>
-            <div>{rows}</div>
-          </>
-        )
+          <QueryData>
+            <div>
+              <div>Tables</div>
+              <div>{}</div>
+            </div>
+            <div>
+              <div>Rows</div>
+              <div>{rows}</div>
+            </div>
+            <div>
+              <div>Datasource Name</div>
+              <div>{}</div>
+            </div>
+          </QueryData>
+        ))
       }
     >
       {loading && (
@@ -241,6 +254,7 @@ function ListViewCard({
             </>
           }
           description={description}
+          avatar={<Icon name={avatar} />}
         />
       )}
     </StyledCard>
diff --git a/superset-frontend/src/components/Menu/SubMenu.tsx b/superset-frontend/src/components/Menu/SubMenu.tsx
index df7e501..93df81c 100644
--- a/superset-frontend/src/components/Menu/SubMenu.tsx
+++ b/superset-frontend/src/components/Menu/SubMenu.tsx
@@ -66,8 +66,9 @@ type MenuClickHandler = React.MouseEventHandler<MenuItem>;
 type MenuChild = {
   label: string;
   name: string;
-  url: string;
+  url?: string;
   usesRouter?: boolean;
+  onClick?: () => void;
 };
 
 export interface ButtonProps {
@@ -131,6 +132,7 @@ const SubMenu: React.FunctionComponent<SubMenuProps> = props => {
                   active={child.name === props.activeChild}
                   key={`${child.label}`}
                   href={child.url}
+                  onClick={child.onClick}
                 >
                   {child.label}
                 </MenuItem>
diff --git a/superset-frontend/src/views/CRUD/chart/ChartList.tsx b/superset-frontend/src/views/CRUD/chart/ChartList.tsx
index a22c54d..0586267 100644
--- a/superset-frontend/src/views/CRUD/chart/ChartList.tsx
+++ b/superset-frontend/src/views/CRUD/chart/ChartList.tsx
@@ -25,7 +25,6 @@ import { createFetchRelated, createErrorHandler } from 'src/views/CRUD/utils';
 import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
 import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
 import SubMenu, { SubMenuProps } from 'src/components/Menu/SubMenu';
-import FacePile from 'src/components/FacePile';
 import Icon from 'src/components/Icon';
 import FaveStar from 'src/components/FaveStar';
 import ListView, {
@@ -36,10 +35,8 @@ import ListView, {
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import PropertiesModal from 'src/explore/components/PropertiesModal';
 import Chart, { Slice } from 'src/types/Chart';
-import ListViewCard from 'src/components/ListViewCard';
-import Label from 'src/components/Label';
-import { Dropdown, Menu } from 'src/common/components';
 import TooltipWrapper from 'src/components/TooltipWrapper';
+import ChartCard from './ChartCard';
 
 const PAGE_SIZE = 25;
 const FAVESTAR_BASE_URL = '/superset/favstar/slice';
@@ -426,69 +423,14 @@ function ChartList(props: ChartListProps) {
     },
   ];
 
-  function renderCard(chart: Chart & { loading: boolean }) {
-    const menu = (
-      <Menu>
-        {canDelete && (
-          <Menu.Item>
-            <ConfirmStatusChange
-              title={t('Please Confirm')}
-              description={
-                <>
-                  {t('Are you sure you want to delete')}{' '}
-                  <b>{chart.slice_name}</b>?
-                </>
-              }
-              onConfirm={() => handleChartDelete(chart)}
-            >
-              {confirmDelete => (
-                <div
-                  data-test="chart-list-delete-option"
-                  role="button"
-                  tabIndex={0}
-                  className="action-button"
-                  onClick={confirmDelete}
-                >
-                  <ListViewCard.MenuIcon name="trash" /> Delete
-                </div>
-              )}
-            </ConfirmStatusChange>
-          </Menu.Item>
-        )}
-        {canEdit && (
-          <Menu.Item
-            data-test="chart-list-edit-option"
-            role="button"
-            tabIndex={0}
-            onClick={() => openChartEditModal(chart)}
-          >
-            <ListViewCard.MenuIcon name="edit-alt" /> Edit
-          </Menu.Item>
-        )}
-      </Menu>
-    );
-
+  function renderCard(chart: Chart) {
     return (
-      <ListViewCard
-        loading={chart.loading}
-        title={chart.slice_name}
-        url={bulkSelectEnabled ? undefined : chart.url}
-        imgURL={chart.thumbnail_url ?? ''}
-        imgFallbackURL="/static/assets/images/chart-card-fallback.png"
-        imgPosition="bottom"
-        description={t('Last modified %s', chart.changed_on_delta_humanized)}
-        coverLeft={<FacePile users={chart.owners || []} />}
-        coverRight={
-          <Label bsStyle="secondary">{chart.datasource_name_text}</Label>
-        }
-        actions={
-          <ListViewCard.Actions>
-            {renderFaveStar(chart.id)}
-            <Dropdown data-test="dropdown-options" overlay={menu}>
-              <Icon name="more-horiz" />
-            </Dropdown>
-          </ListViewCard.Actions>
-        }
+      <ChartCard
+        chart={chart}
+        hasPerm={hasPerm}
+        openChartEditModal={openChartEditModal}
+        bulkSelectEnabled={bulkSelectEnabled}
+        renderFaveStar={renderFaveStar}
       />
     );
   }
diff --git a/superset-frontend/src/views/CRUD/utils.tsx b/superset-frontend/src/views/CRUD/utils.tsx
index 213481d..0d5550e 100644
--- a/superset-frontend/src/views/CRUD/utils.tsx
+++ b/superset-frontend/src/views/CRUD/utils.tsx
@@ -53,6 +53,22 @@ const createFetchResourceMethod = (method: string) => (
   return [];
 };
 
+export const createBatchMethod = (queryParams: string) => {
+  return Promise.all([
+    SupersetClient.get({ endpoint: `/api/v1/dashboard/?q=${queryParams}` }),
+    SupersetClient.get({ endpoint: `/api/v1/chart/?q=${queryParams}` }),
+  ]).then(([dashboardRes, chartRes]) => {
+    const results = [];
+    results.push(
+      ...[
+        ...dashboardRes.json.result.slice(0, 3),
+        ...chartRes.json.result.slice(0, 3),
+      ],
+    );
+    return results;
+  });
+};
+
 export const createFetchRelated = createFetchResourceMethod('related');
 export const createFetchDistinct = createFetchResourceMethod('distinct');
 
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index 9203848..ae496f2 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -18,12 +18,16 @@
  */
 import React, { useEffect, useState } from 'react';
 import { styled, SupersetClient, t } from '@superset-ui/core';
+import Icon from 'src/components/Icon';
 import rison from 'rison';
 import moment from 'moment';
+import ListViewCard from 'src/components/ListViewCard';
 import { addDangerToast } from 'src/messageToasts/actions';
+import { MenuItem } from 'react-bootstrap';
 import { createBatchMethod, createErrorHandler } from '../utils';
 
 interface MapProps {
+  action: string;
   item_title: string;
   slice_name: string;
   time: string;
@@ -81,6 +85,14 @@ export default function ActivityTable({ user, activityFilter }: MapProps) {
     });
   };
 
+  const getIconName = (name: string): string => {
+    console.log('name', name)
+    if (name === 'explore_json') return 'sql';
+    if (name === 'dashboard') return 'nav-dashboard';
+    if (name === 'log') return 'nav-chart';
+    return 'sql';
+  };
+
   const getData = () => {
     const queryParams = rison.encode({
       order_column: 'changed_on_delta_humanized',
@@ -105,7 +117,23 @@ export default function ActivityTable({ user, activityFilter }: MapProps) {
   }, [activityFilter]);
 
   const renderActivity = () => {
+    console.log('e', active)
     return active.map((e: MapProps) => (
+      <ListViewCard
+        isRecent={true}
+        url={e.item_url}
+        title={activityFilter === 'Viewed' ? e.item_title : e.slice_name}
+        description={moment
+          .utc(activityFilter === 'Viewd' ? e.time : e.changed_on_utc)
+          .fromNow()}
+        avatar={getIconName(e.action)}
+      />
+    ));
+  };
+
+  return <> {renderActivity()} </>;
+}
+/*
       <a href={e.item_url}>
         <Cards>
           <div>{activityFilter === 'Viewed' ? e.item_title : e.slice_name}</div>
@@ -115,9 +143,5 @@ export default function ActivityTable({ user, activityFilter }: MapProps) {
               .fromNow()}
           </div>
         </Cards>
-      </a>
-    ));
-  };
-
-  return <> {renderActivity()} </>;
-}
+      </>
+      */
\ No newline at end of file
diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
index 9b8529e..1958f88 100644
--- a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
@@ -95,9 +95,13 @@ function ChartTable({
       });
     } else {
       filters.push({
-        id: 'favorite', // API currently can't filter by favorite
+        id: 'id',
+        operator: 'chart_is_fav',
+        value: true,
+       /* id: 'favorite', // API currently can't filter by favorite
         operator: 'eq',
         value: true,
+        */
       });
     }
     // Do we need search?
@@ -127,7 +131,7 @@ function ChartTable({
   }, [chartFilter]);
   console.log("----charts: ", charts);
   return (
-    <div>
+    <>
       {charts.map(e => (
         <DashboardCard
           {...{
@@ -141,7 +145,7 @@ function ChartTable({
           isChart
         />
       ))}
-    </div>
+    </>
   );
 }
 
diff --git a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
index ca3f584..0167006 100644
--- a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import React, { useEffect } from 'react';
-import { t, styled } from '@superset-ui/core';
+import { t } from '@superset-ui/core';
 import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import { User } from 'src/types/bootstrapTypes';
@@ -71,6 +71,10 @@ function DashboardTable({
     t('dashboard'),
     addDangerToast,
   );
+
+  console.log('dashboards', dashboards);
+  console.log('dashboardFilter', dashboardFilter);
+
   const getFilters = () => {
     const filters = [];
 
@@ -82,8 +86,8 @@ function DashboardTable({
       });
     } else {
       filters.push({
-        id: 'favorite', // API currently can't filter by favorite
-        operator: 'eq',
+        id: 'id',
+        operator: 'dashboard_is_fav',
         value: true,
       });
     }
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
index 116cf91..cab929e 100644
--- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -18,8 +18,8 @@
  */
 import React from 'react';
 import { t, SupersetClient } from '@superset-ui/core';
-// import withToasts from 'src/messageToasts/enhancers/withToasts';
-import { Dropdown } from 'src/common/components';
+import withToasts from 'src/messageToasts/enhancers/withToasts';
+import { Dropdown, Menu } from 'src/common/components';
 import ListViewCard from 'src/components/ListViewCard';
 // import FaveStar from 'src/components/FaveStar';
 import Icon from 'src/components/Icon';
@@ -35,9 +35,11 @@ class SavedQueries extends React.PureComponent {
       queries: [],
     };
   }
+
   componentDidMount() {
     this.fetchData();
   }
+
   // eslint-disable-next-line consistent-return
   fetchData = async () => {
     try {
@@ -49,7 +51,14 @@ class SavedQueries extends React.PureComponent {
       return console.log(e);
     }
   };
+
   render() {
+    const menu = (
+      <Menu>
+        <Menu.Item>Delete</Menu.Item>
+      </Menu>
+    );
+    // console.log('queries', this.state.queries)
     return (
       <>
         {this.state.queries.map(q => (
@@ -61,8 +70,8 @@ class SavedQueries extends React.PureComponent {
             showImg={false}
             actions={
               <ListViewCard.Actions>
-                <Dropdown>
-                  <Icon name="more" />
+                <Dropdown overlay={menu}>
+                  <Icon name="more-horiz" />
                 </Dropdown>
               </ListViewCard.Actions>
             }
@@ -73,4 +82,4 @@ class SavedQueries extends React.PureComponent {
   }
 }
 
-export default SavedQueries;
+export default withToasts(SavedQueries);
diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index 5b05075..1cd6db9 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -23,7 +23,8 @@ import { styled, t } from '@superset-ui/core';
 import { Collapse } from 'src/common/components';
 import { useQueryParam, StringParam, QueryParamConfig } from 'use-query-params';
 import { User } from 'src/types/bootstrapTypes';
-import RecentActivity from 'src/profile/components/RecentActivity';
+import Icon from 'src/components/Icon';
+import ActivityTable from './ActivityTable';
 import ChartTable from './ChartTable';
 import SavedQueries from './SavedQueries';
 import DashboardTable from './DashboardTable';
@@ -43,11 +44,16 @@ const ActivityContainer = styled.div`
     ${({ theme }) => theme.gridUnit * 4}px;
 `;
 
+const IconContainer = styled.div`
+  svg {
+    vertical-align: -7px;
+  }
+`;
 export const CardContainer = styled.div`
   display: grid;
-  grid-template-columns: repeat(auto-fit, minmax(459px, max-content));
+  grid-template-columns: repeat(auto-fit, minmax(459px, 1fr));
   grid-gap: ${({ theme }) => theme.gridUnit * 8}px;
-  justify-content: center;
+  justify-content: left;
   padding: ${({ theme }) => theme.gridUnit * 2}px
     ${({ theme }) => theme.gridUnit * 4}px;
 `;
@@ -71,16 +77,14 @@ function useSyncQueryState(
   return [state, setQueryStateAndState];
 }
 
-function ding(e: any) {
-  console.log('event', e);
-}
-
 export default function Welcome({ user }: WelcomeProps) {
   const [activeTab, setActiveTab] = useSyncQueryState(
     'activeTab',
     StringParam,
     'all',
   );
+  const [queryFilter, setQueryFilter] = useState('Favorite');
+  const [activityFilter, setActivityFilter] = useState('Viewed');
   const [dashboardFilter, setDashboardFilter] = useState('Favorite');
   const [chartFilter, setChartFilter] = useState('Favorite');
   const [searchQuery, setSearchQuery] = useSyncQueryState(
@@ -93,29 +97,33 @@ export default function Welcome({ user }: WelcomeProps) {
     <Collapse defaultActiveKey={['1']}>
       <Panel header={t('Recents')} key="1">
         <SubMenu
-          activeChild={activtyFilter}
+          activeChild={activityFilter}
           name=""
           // eslint-disable-next-line react/no-children-prop
           children={[
             {
               name: 'Viewed',
               label: t('Viewed'),
-              onClick: () => ding('hi?'),
+              onClick: () => setActivityFilter('Viewed'),
             },
             {
               name: 'Edited',
               label: t('Edited'),
-              onClick: ding,
+              onClick: () => setActivityFilter('Edited'),
             },
             {
               name: 'Created',
               label: t('Created'),
-              onClick: ding,
+              onClick: () => setActivityFilter('Created'),
             },
           ]}
+          links={{
+            link: `/chart/list`,
+            linkTitle: 'View All',
+          }}
         />
         <ActivityContainer>
-          <ActivityTable user={user} activityFilter={activtyFilter} />
+          <ActivityTable user={user} activityFilter={activityFilter} />
         </ActivityContainer>
       </Panel>
 
@@ -136,6 +144,24 @@ export default function Welcome({ user }: WelcomeProps) {
               onClick: () => setDashboardFilter('Mine'),
             },
           ]}
+          buttons={[
+            {
+              name: (
+                <IconContainer>
+                  <Icon name="plus-small" /> DashBoard{' '}
+                </IconContainer>
+              ),
+              buttonStyle: 'tertiary',
+              onClick: () => {
+                window.location = '/dashboard/new';
+              },
+            },
+            {
+              name: 'View All',
+              buttonStyle: 'link',
+              onClick: () => { window.location = '/dashboard/list/'}
+            },
+          ]}
         />
         <FormControl
           type="text"
@@ -156,19 +182,37 @@ export default function Welcome({ user }: WelcomeProps) {
 
       <Panel header={t('Saved Queries')} key="3">
         <SubMenu
-          activeChild={dashboardFilter}
+          activeChild={queryFilter}
           name=""
           // eslint-disable-next-line react/no-children-prop
           children={[
             {
               name: 'Favorite',
               label: t('Favorite'),
-              onClick: () => setChartFilter('Favorite'),
+              onClick: () => setQueryFilter('Favorite'),
             },
             {
               name: 'Mine',
               label: t('Mine'),
-              onClick: () => setChartFilter('Mine'),
+              onClick: () => setQueryFilter('Mine'),
+            },
+          ]}
+          buttons={[
+            {
+              name: (
+                <IconContainer>
+                  <Icon name="plus-small" /> SQL Query{' '}
+                </IconContainer>
+              ),
+              buttonStyle: 'tertiary',
+              onClick: () => {
+                window.location = '/superset/sqllab';
+              },
+            },
+            {
+              name: 'View All',
+              buttonStyle: 'link',
+              onClick: () => { window.location = 'superset/sqllab#search'}
             },
           ]}
         />
@@ -193,8 +237,29 @@ export default function Welcome({ user }: WelcomeProps) {
               onClick: () => setChartFilter('Mine'),
             },
           ]}
+          buttons={[
+            {
+              name: (
+                <IconContainer>
+                  <Icon name="plus-small" /> Chart{' '}
+                </IconContainer>
+              ),
+              buttonStyle: 'tertiary',
+              onClick: () => {
+                window.location = '/chart/add';
+              },
+            },
+            {
+              name: 'View All',
+              buttonStyle: 'link',
+              onClick: () => { window.location = '/chart/list'}
+            },
+          ]}
         />
-        <ChartTable chartFilter={chartFilter} user={user} />
+
+        <CardContainer>
+          <ChartTable chartFilter={chartFilter} user={user} />
+        </CardContainer>
       </Panel>
     </Collapse>
   );


[incubator-superset] 11/33: clean up code

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 08470384645a4929cce1074364ad15bcd1485e29
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Wed Oct 7 09:40:05 2020 -0700

    clean up code
---
 .../src/views/CRUD/chart/ChartCard.tsx             | 10 +----
 .../src/views/CRUD/chart/ChartList.tsx             | 35 +++++------------
 superset-frontend/src/views/CRUD/hooks.ts          | 45 ++++++++++++++++++++++
 superset-frontend/src/views/CRUD/types.ts          |  4 +-
 .../src/views/CRUD/welcome/ActivityTable.tsx       | 44 +++++++++++----------
 .../src/views/CRUD/welcome/ChartTable.tsx          | 43 ++++++++++-----------
 .../src/views/CRUD/welcome/DashboardTable.tsx      | 15 +-------
 .../src/views/CRUD/welcome/SavedQueries.tsx        | 40 ++++++++++---------
 8 files changed, 126 insertions(+), 110 deletions(-)

diff --git a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
index a8a123c..1046f3a 100644
--- a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
+++ b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
@@ -60,15 +60,6 @@ export default function ChartCard({
     addDangerToast,
   );
 
-  function openChartEditModal(chart: Chart) {
-    setSliceCurrentlyEditing({
-      slice_id: chart.id,
-      slice_name: chart.slice_name,
-      description: chart.description,
-      cache_timeout: chart.cache_timeout,
-    });
-  }
-
   function handleChartDelete({ id, slice_name: sliceName }: Chart) {
     SupersetClient.delete({
       endpoint: `/api/v1/chart/${id}`,
@@ -121,6 +112,7 @@ export default function ChartCard({
       )}
     </Menu>
   );
+  console.log('favorite status', favoriteStatusRef)
   return (
     <ListViewCard
       loading={loading}
diff --git a/superset-frontend/src/views/CRUD/chart/ChartList.tsx b/superset-frontend/src/views/CRUD/chart/ChartList.tsx
index d326db7..ec0722e 100644
--- a/superset-frontend/src/views/CRUD/chart/ChartList.tsx
+++ b/superset-frontend/src/views/CRUD/chart/ChartList.tsx
@@ -22,7 +22,11 @@ import rison from 'rison';
 import { uniqBy } from 'lodash';
 import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
 import { createFetchRelated, createErrorHandler } from 'src/views/CRUD/utils';
-import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
+import {
+  useListViewResource,
+  useFavoriteStatus,
+  useChartEditModal,
+} from 'src/views/CRUD/hooks';
 import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
 import SubMenu, { SubMenuProps } from 'src/components/Menu/SubMenu';
 import Icon from 'src/components/Icon';
@@ -102,37 +106,18 @@ function ChartList(props: ChartListProps) {
     FAVESTAR_BASE_URL,
     props.addDangerToast,
   );
-  const [
+  const {
     sliceCurrentlyEditing,
-    setSliceCurrentlyEditing,
-  ] = useState<Slice | null>(null);
+    handleChartUpdated,
+    openChartEditModal,
+    closeChartEditModal,
+  } = useChartEditModal(setCharts, charts);
 
   const canCreate = hasPerm('can_add');
   const canEdit = hasPerm('can_edit');
   const canDelete = hasPerm('can_delete');
   const initialSort = [{ id: 'changed_on_delta_humanized', desc: true }];
 
-  function openChartEditModal(chart: Chart) {
-    setSliceCurrentlyEditing({
-      slice_id: chart.id,
-      slice_name: chart.slice_name,
-      description: chart.description,
-      cache_timeout: chart.cache_timeout,
-    });
-  }
-
-  function closeChartEditModal() {
-    setSliceCurrentlyEditing(null);
-  }
-
-  function handleChartUpdated(edits: Chart) {
-    // update the chart in our state with the edited info
-    const newCharts = charts.map(chart =>
-      chart.id === edits.id ? { ...chart, ...edits } : chart,
-    );
-    setCharts(newCharts);
-  }
-
   function handleChartDelete({ id, slice_name: sliceName }: Chart) {
     SupersetClient.delete({
       endpoint: `/api/v1/chart/${id}`,
diff --git a/superset-frontend/src/views/CRUD/hooks.ts b/superset-frontend/src/views/CRUD/hooks.ts
index 9c2386f..80de0a7 100644
--- a/superset-frontend/src/views/CRUD/hooks.ts
+++ b/superset-frontend/src/views/CRUD/hooks.ts
@@ -21,7 +21,9 @@ import { useState, useEffect, useCallback, useRef } from 'react';
 import { SupersetClient, t } from '@superset-ui/core';
 
 import { createErrorHandler } from 'src/views/CRUD/utils';
+import { Slice } from 'src/explore/components/PropertiesModal';
 import { FetchDataConfig } from 'src/components/ListView';
+import Chart from 'src/types/Chart';
 import { FavoriteStatus } from './types';
 
 interface ListViewResourceState<D extends object = any> {
@@ -33,6 +35,11 @@ interface ListViewResourceState<D extends object = any> {
   bulkSelectEnabled: boolean;
 }
 
+interface EditChartModal {
+  setCharts: () => void;
+  charts: Array<any>;
+}
+
 export function useListViewResource<D extends object = any>(
   resource: string,
   resourceLabel: string, // resourceLabel for translations
@@ -352,3 +359,41 @@ export function useFavoriteStatus(
 
   return [favoriteStatusRef, fetchFaveStar, saveFaveStar] as const;
 }
+
+export const useChartEditModal = (
+  setCharts: (charts: Array<Chart>) => void,
+  charts: Array<Chart>,
+) => {
+  const [
+    sliceCurrentlyEditing,
+    setSliceCurrentlyEditing,
+  ] = useState<Slice | null>(null);
+
+  function openChartEditModal(chart: Chart) {
+    setSliceCurrentlyEditing({
+      slice_id: chart.id,
+      slice_name: chart.slice_name,
+      description: chart.description,
+      cache_timeout: chart.cache_timeout,
+    });
+  }
+
+  function closeChartEditModal() {
+    setSliceCurrentlyEditing(null);
+  }
+
+  function handleChartUpdated(edits: Chart) {
+    // update the chart in our state with the edited info
+    const newCharts = charts.map((chart: Chart) =>
+      chart.id === edits.id ? { ...chart, ...edits } : chart,
+    );
+    setCharts(newCharts);
+  }
+
+  return {
+    sliceCurrentlyEditing,
+    handleChartUpdated,
+    openChartEditModal,
+    closeChartEditModal,
+  };
+};
diff --git a/superset-frontend/src/views/CRUD/types.ts b/superset-frontend/src/views/CRUD/types.ts
index 7f5fce5..afd1956 100644
--- a/superset-frontend/src/views/CRUD/types.ts
+++ b/superset-frontend/src/views/CRUD/types.ts
@@ -31,7 +31,7 @@ export interface DashboardTableProps {
   user?: User;
 }
 
-interface Dashboard {
+export interface Dashboard {
   changed_by_name: string;
   changed_by_url: string;
   changed_on_delta_humanized: string;
@@ -43,7 +43,7 @@ interface Dashboard {
   url: string;
   thumbnail_url: string;
   owners: Owner[];
-  loading: boolean;
+  loading?: boolean;
 }
 
 export interface DashboardCardProps {
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index b4c941c..de237b4 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -40,34 +40,35 @@ interface ActivityProps {
   activityFilter: string;
 }
 
-const filters = {
-  // Chart and dashbaord uses same filters
-  // for edited and created
-  edited: [
-    {
-      col: 'changed_by',
-      opr: 'rel_o_m',
-      value: 1,
-    },
-  ],
-  created: [
-    {
-      col: 'owners',
-      opr: 'rel_m_m',
-      value: 1,
-    },
-  ],
-};
-
 export default function ActivityTable({ user, activityFilter }: ActivityProps) {
   const [active, setActiveState] = useState([]);
   const [loading, setLoading] = useState(false);
-  const recent = `/superset/recent_activity/${user.userId}/?limit=10`;
+  const recent = `/superset/recent_activity/${user.userId}/?limit=5`;
+  const filters = {
+    // Chart and dashbaord uses same filters
+    // for edited and created
+    edited: [
+      {
+        col: 'changed_by',
+        opr: 'rel_o_m',
+        value: `${user.userId}`,
+      },
+    ],
+    created: [
+      {
+        col: 'created_by',
+        opr: 'rel_o_m',
+        value: `${user.userId}`,
+      },
+    ],
+  };
+
   const setData = (endpoint: string) => {
     setLoading(true);
     SupersetClient.get({ endpoint })
       .then(({ json }) => {
         setLoading(false);
+        // @ts-ignore
         setActiveState(json);
       })
       .catch(() => {
@@ -79,13 +80,14 @@ export default function ActivityTable({ user, activityFilter }: ActivityProps) {
   };
 
   const setBatchData = (q: string) => {
+    // @ts-ignore
     createBatchMethod(q).then((res: Array<object>) => setActiveState(res));
   };
 
   const getIconName = (name: string | undefined) => {
     if (name === 'explore_json') return 'sql';
     if (name === 'dashboard') return 'nav-dashboard';
-    if (name === 'log') return 'nav-charts';
+    if (name === 'log' || name === 'explore') return 'nav-charts';
     return '';
   };
 
diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
index e1223cb..d0ccaee 100644
--- a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
@@ -18,8 +18,9 @@
  */
 import React, { useEffect } from 'react';
 import { t } from '@superset-ui/core';
-import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
+import { useListViewResource, useChartEditModal } from 'src/views/CRUD/hooks';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
+import PropertiesModal from 'src/explore/components/PropertiesModal';
 import { User } from 'src/types/bootstrapTypes';
 import Owner from 'src/types/Owner';
 import ChartCard from 'src/views/CRUD/chart/ChartCard';
@@ -35,27 +36,6 @@ interface ChartTableProps {
   user?: User;
 }
 
-interface Dashboard {
-  changed_by_name: string;
-  changed_by_url: string;
-  changed_on_delta_humanized: string;
-  changed_by: string;
-  dashboard_title: string;
-  slice_name: string;
-  id: number;
-  published: boolean;
-  url: string;
-  thumbnail_url: string;
-  owners: Owner[];
-  loading: boolean;
-}
-
-interface ChartTableState {
-  charts: Dashboard[];
-  chart_count: number;
-  loading: boolean;
-}
-
 function ChartTable({
   chartFilter,
   user,
@@ -64,10 +44,19 @@ function ChartTable({
 }: ChartTableProps) {
   const {
     state: { loading, resourceCollection: charts, bulkSelectEnabled },
+    setResourceCollection: setCharts,
     hasPerm,
     refreshData,
     fetchData,
   } = useListViewResource<Chart>('chart', t('chart'), addDangerToast);
+
+  const {
+    sliceCurrentlyEditing,
+    openChartEditModal,
+    handleChartUpdated,
+    closeChartEditModal,
+  } = useChartEditModal(setCharts, charts);
+
   const getFilters = () => {
     const filters = [];
 
@@ -112,9 +101,19 @@ function ChartTable({
 
   return (
     <>
+      {sliceCurrentlyEditing && (
+        <PropertiesModal
+          onHide={closeChartEditModal}
+          onSave={handleChartUpdated}
+          show
+          slice={sliceCurrentlyEditing}
+        />
+      )}
+
       {charts.map((e, i) => (
         <ChartCard
           key={`${i}`}
+          openChartEditModal={openChartEditModal}
           loading={loading}
           chart={e}
           hasPerm={hasPerm}
diff --git a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
index 0167006..c47b2dd 100644
--- a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
@@ -20,20 +20,12 @@ import React, { useEffect } from 'react';
 import { t } from '@superset-ui/core';
 import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
-import { User } from 'src/types/bootstrapTypes';
 import Owner from 'src/types/Owner';
-import DashboardCard from '../dashboard/DashboardCard';
+import { DashboardTableProps } from 'src/views/CRUD/types';
+import DashboardCard from 'src/views/CRUD/dashboard/DashboardCard';
 
 const PAGE_SIZE = 3;
 
-interface DashboardTableProps {
-  addDangerToast: (message: string) => void;
-  addSuccessToast: (message: string) => void;
-  search: string;
-  dashboardFilter?: string;
-  user?: User;
-}
-
 interface Dashboard {
   changed_by_name: string;
   changed_by_url: string;
@@ -72,9 +64,6 @@ function DashboardTable({
     addDangerToast,
   );
 
-  console.log('dashboards', dashboards);
-  console.log('dashboardFilter', dashboardFilter);
-
   const getFilters = () => {
     const filters = [];
 
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
index 73c7547..d30db0b 100644
--- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -87,24 +87,28 @@ const SavedQueries = ({ user, queryFilter }) => {
 
   return (
     <>
-      {queries.map(q => (
-        <ListViewCard
-          imgFallbackURL={null}
-          imgURL={null}
-          title={q.database.database_name}
-          rows={q.rows}
-          loading={loading}
-          description={t('Last run ', q.end_time)}
-          showImg={false}
-          actions={
-            <ListViewCard.Actions>
-              <Dropdown overlay={menu}>
-                <Icon name="more-horiz" />
-              </Dropdown>
-            </ListViewCard.Actions>
-          }
-        />
-      ))}
+      {queries ? (
+        queries.map(q => (
+          <ListViewCard
+            imgFallbackURL={null}
+            imgURL={null}
+            title={q.database.database_name}
+            rows={q.rows}
+            loading={loading}
+            description={t('Last run ', q.end_time)}
+            showImg={false}
+            actions={
+              <ListViewCard.Actions>
+                <Dropdown overlay={menu}>
+                  <Icon name="more-horiz" />
+                </Dropdown>
+              </ListViewCard.Actions>
+            }
+          />
+        ))
+      ) : (
+        <span>You have no Saved Queries!</span>
+      )}
     </>
   );
 };


[incubator-superset] 18/33: remove consoles

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 4ae640e054e9593b4be74a030bc508f4838fc48e
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Mon Oct 19 14:40:52 2020 -0700

    remove consoles
---
 .../spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx            | 1 -
 superset-frontend/src/views/CRUD/hooks.ts                              | 3 ---
 2 files changed, 4 deletions(-)

diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx
index b6e5175..b4eb6d1 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/EmptyState_spec.tsx
@@ -31,6 +31,5 @@ describe('EmptyState', () => {
     };
     const wrapper = mount(<EmptyState {...props} />);
     expect(wrapper).toExist();
-    console.log('wrapper', wrapper.debug());
   });
 });
diff --git a/superset-frontend/src/views/CRUD/hooks.ts b/superset-frontend/src/views/CRUD/hooks.ts
index 9dbdbc2..08e1262 100644
--- a/superset-frontend/src/views/CRUD/hooks.ts
+++ b/superset-frontend/src/views/CRUD/hooks.ts
@@ -330,7 +330,6 @@ export function useFavoriteStatus(
       endpoint: `${baseURL}/${id}/count/`,
     }).then(
       ({ json }) => {
-        console.log('json', json);
         updateFavoriteStatus({ [id]: json.count > 0 });
       },
       createErrorHandler(errMsg =>
@@ -439,8 +438,6 @@ export const copyQueryLink = (
     } else {
       selection.removeAllRanges();
     }
-    console.log('-----Success Toast--------')
     addSuccessToast(t('Link Copied!'));
-    console.log('-----Success Toast--------')
   }
 };


[incubator-superset] 32/33: test fixes

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 03e6fde0a52f565ad5bed891064b4205c866122b
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Mon Oct 26 15:51:59 2020 -0700

    test fixes
---
 .../spec/javascripts/components/SubMenu_spec.jsx   |  2 +-
 .../views/CRUD/welcome/ActivityTable_spec.tsx      |  6 ++--
 .../views/CRUD/welcome/ChartTable_spec.tsx         | 39 +++++++++++++---------
 .../views/CRUD/welcome/DashboardTable_spec.tsx     |  7 ++--
 .../views/CRUD/welcome/Welcome_spec.tsx            | 18 +++++++---
 .../src/views/CRUD/chart/ChartCard.tsx             |  3 +-
 superset-frontend/src/views/CRUD/data/common.ts    |  2 +-
 superset-frontend/src/views/CRUD/utils.tsx         | 13 +++++++-
 .../src/views/CRUD/welcome/ActivityTable.tsx       |  2 +-
 .../src/views/CRUD/welcome/ChartTable.tsx          |  3 +-
 10 files changed, 62 insertions(+), 33 deletions(-)

diff --git a/superset-frontend/spec/javascripts/components/SubMenu_spec.jsx b/superset-frontend/spec/javascripts/components/SubMenu_spec.jsx
index 54a1b63..ab0020d 100644
--- a/superset-frontend/spec/javascripts/components/SubMenu_spec.jsx
+++ b/superset-frontend/spec/javascripts/components/SubMenu_spec.jsx
@@ -24,7 +24,7 @@ import SubMenu from 'src/components/Menu/SubMenu';
 
 const defaultProps = {
   name: 'Title',
-  children: [
+  tabs: [
     {
       name: 'Page1',
       label: 'Page1',
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx
index 19fb397..db1ec1f 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx
@@ -29,6 +29,7 @@ import ListViewCard from 'src/components/ListViewCard';
 const mockStore = configureStore([thunk]);
 const store = mockStore({});
 
+const recentsEnpoint = 'glob:*/superset/recent_activity/*';
 const chartsEndpoint = 'glob:*/api/v1/chart/?*';
 const dashboardEndpoint = 'glob:*/api/v1/dashboard/?*';
 const savedQueryEndpoint = 'glob:*/api/v1/saved_query/?*';
@@ -75,15 +76,14 @@ describe('ActivityTable', () => {
     await waitForComponentToPaint(wrapper);
   });
 
-  it('renders', () => {
+  it('the component renders ', () => {
     expect(wrapper.find(ActivityTable)).toExist();
   });
 
-  it('calls batch method and renders ListViewCArd', () => {
+  it('calls batch method and renders ListViewCArd', async () => {
     const chartCall = fetchMock.calls(/chart\/\?q/);
     const dashboardCall = fetchMock.calls(/dashboard\/\?q/);
     expect(chartCall).toHaveLength(2);
     expect(dashboardCall).toHaveLength(2);
-    expect(wrapper.find(ListViewCard)).toHaveLength(2);
   });
 });
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx
index aa024d8..f8cd053 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/ChartTable_spec.tsx
@@ -17,20 +17,19 @@
  * under the License.
  */
 import React from 'react';
-import { mount } from 'enzyme';
+import { styledMount as mount } from 'spec/helpers/theming';
 import thunk from 'redux-thunk';
 import fetchMock from 'fetch-mock';
-
 import configureStore from 'redux-mock-store';
+
 import ChartTable from 'src/views/CRUD/welcome/ChartTable';
-import ChartCard from 'src/views/CRUD/chart/ChartCard';
 import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
 
-// store needed for withToasts(DashboardTable)
 const mockStore = configureStore([thunk]);
 const store = mockStore({});
 
 const chartsEndpoint = 'glob:*/api/v1/chart/?*';
+const chartsInfoEndpoint = 'glob:*/api/v1/chart/_info*';
 
 const mockCharts = [...new Array(3)].map((_, i) => ({
   changed_on_utc: new Date().toISOString(),
@@ -40,31 +39,41 @@ const mockCharts = [...new Array(3)].map((_, i) => ({
   url: 'url',
   viz_type: 'bar',
   datasource_title: `ds${i}`,
-  thumbnail_url: '/thumbnail',
+  thumbnail_url: '',
 }));
 
 fetchMock.get(chartsEndpoint, {
   result: mockCharts,
 });
 
-describe('ChartTable', () => {
-  beforeEach(fetchMock.resetHistory);
+fetchMock.get(chartsInfoEndpoint, {
+  permissions: ['can_add', 'can_edit', 'can_delete'],
+});
 
-  const mockedProps = {};
+describe('ChartTable', () => {
+  const mockedProps = {
+    user: {
+      userId: '2',
+    },
+  };
   const wrapper = mount(<ChartTable {...mockedProps} />, {
     context: { store },
   });
-
-  beforeAll(async () => {
-    await waitForComponentToPaint(wrapper);
-  });
-
   it('it renders', () => {
     expect(wrapper.find(ChartTable)).toExist();
   });
 
-  it('fetches chart favorites and renders chart cards ', () => {
+  it('fetches chart favorites and renders chart cards ', async () => {
     expect(fetchMock.calls(chartsEndpoint)).toHaveLength(1);
-    expect(wrapper.find(ChartCard)).toExist();
+    await waitForComponentToPaint(wrapper);
+    expect(wrapper.find('ChartCard')).toExist();
+  });
+
+  it('display EmptyState if there is no data', () => {
+    fetchMock.resetHistory();
+    const wrapper = mount(<ChartTable {...mockedProps} />, {
+      context: { store },
+    });
+    expect(wrapper.find('EmptyState')).toExist();
   });
 });
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx
index 013d3b3..e7f28f3 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/DashboardTable_spec.tsx
@@ -33,7 +33,7 @@ const mockStore = configureStore([thunk]);
 const store = mockStore({});
 
 const dashboardsEndpoint = 'glob:*/api/v1/dashboard/?*';
-const chartsInfoEndpoint = 'glob:*/api/v1/chart/_info*';
+const dashboardInfoEndpoint = 'glob:*/api/v1/dashboard/_info*';
 const mockDashboards = [
   {
     id: 1,
@@ -44,7 +44,7 @@ const mockDashboards = [
 ];
 
 fetchMock.get(dashboardsEndpoint, { result: mockDashboards });
-fetchMock.get(chartsInfoEndpoint, {
+fetchMock.get(dashboardInfoEndpoint, {
   permissions: ['can_list', 'can_edit', 'can_delete'],
 });
 
@@ -86,6 +86,9 @@ describe('DashboardTable', () => {
 
   it('display EmptyState if there is no data', () => {
     fetchMock.resetHistory();
+    const wrapper = mount(<DashboardTable {...dashboardProps} />, {
+      context: { store },
+    });
     expect(wrapper.find('EmptyState')).toExist();
   });
 });
diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
index fa1778d..a9a2c49 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
@@ -17,10 +17,14 @@
  * under the License.
  */
 import React from 'react';
-import { styledMount as mount } from 'spec/helpers/theming';
-
+import { shallow } from 'enzyme';
+import thunk from 'redux-thunk';
+import configureStore from 'redux-mock-store';
 import Welcome from 'src/views/CRUD/welcome/Welcome';
 
+const mockStore = configureStore([thunk]);
+const store = mockStore({});
+
 describe('Welcome', () => {
   const mockedProps = {
     user: {
@@ -33,11 +37,15 @@ describe('Welcome', () => {
       isActive: true,
     },
   };
-  const wrapper = mount(<Welcome {...mockedProps} />);
+  const wrapper = shallow(<Welcome {...mockedProps} />, {
+    context: { store },
+  });
+
   it('is renders', () => {
-    expect(wrapper.find(Welcome)).toExist();
+    expect(wrapper).toExist();
   });
+
   it('renders all panels on the page on page load', () => {
-    expect(wrapper.find('PanelContent')).toHaveLength(4);
+    expect(wrapper.find('CollapsePanel')).toHaveLength(4);
   });
 });
diff --git a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
index 9e3b696..bcf21d2 100644
--- a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
+++ b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
@@ -59,7 +59,6 @@ export default function ChartCard({
     FAVESTAR_BASE_URL,
     addDangerToast,
   );
-
   function handleChartDelete({ id, slice_name: sliceName }: Chart) {
     SupersetClient.delete({
       endpoint: `/api/v1/chart/${id}`,
@@ -117,7 +116,7 @@ export default function ChartCard({
       loading={loading}
       title={chart.slice_name}
       url={bulkSelectEnabled ? undefined : chart.url}
-      imgURL={chart.thumbnail_url ?? ''}
+      imgURL={chart.thumbnail_url || ''}
       imgFallbackURL="/static/assets/images/chart-card-fallback.png"
       description={t('Last modified %s', chart.changed_on_delta_humanized)}
       coverLeft={<FacePile users={chart.owners || []} />}
diff --git a/superset-frontend/src/views/CRUD/data/common.ts b/superset-frontend/src/views/CRUD/data/common.ts
index 9b2194a..6fecd21 100644
--- a/superset-frontend/src/views/CRUD/data/common.ts
+++ b/superset-frontend/src/views/CRUD/data/common.ts
@@ -20,7 +20,7 @@ import { t } from '@superset-ui/core';
 
 export const commonMenuData = {
   name: t('Data'),
-  children: [
+  tabs: [
     {
       name: 'Datasets',
       label: t('Datasets'),
diff --git a/superset-frontend/src/views/CRUD/utils.tsx b/superset-frontend/src/views/CRUD/utils.tsx
index 55ae299..a604770 100644
--- a/superset-frontend/src/views/CRUD/utils.tsx
+++ b/superset-frontend/src/views/CRUD/utils.tsx
@@ -101,15 +101,26 @@ export const getRecentAcitivtyObjs = (
     SupersetClient.get({
       endpoint: `/api/v1/chart/?q=${getParams(filters.created)}`,
     }),
+    SupersetClient.get({
+      endpoint: `/api/v1/saved_query/?q=${getParams(filters.created)}`,
+    }),
   ];
   return Promise.all(baseBatch).then(
     // @ts-ignore
-    ([recentsRes, editedDash, editedChart, createdByDash, createdByChart]) => {
+    ([
+      recentsRes,
+      editedDash,
+      editedChart,
+      createdByDash,
+      createdByChart,
+      createdByQuery,
+    ]) => {
       const res: any = {
         editedDash: editedDash.json?.result.slice(0, 3),
         editedChart: editedChart.json?.result.slice(0, 3),
         createdByDash: createdByDash.json?.result.slice(0, 3),
         createdByChart: createdByChart.json?.result.slice(0, 3),
+        createdByQuery: createdByQuery.json?.result.slice(0, 3),
       };
       if (recentsRes.json.length === 0) {
         const newBatch = [
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index 56e5d70..c515911 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -147,7 +147,7 @@ export default function ActivityTable({ user }: ActivityProps) {
     getRecentAcitivtyObjs(user.userId, recent)
       .then(res => {
         const data: any = {
-          Created: [...res.createdByChart, ...res.createdByDash],
+          Created: [...res.createdByChart, ...res.createdByDash, ...res.createdByQuery],
           Edited: [...res.editedChart, ...res.editedDash],
         };
         if (res.viewed) {
diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
index 1979a18..c807f35 100644
--- a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
@@ -51,7 +51,6 @@ function ChartTable({
     refreshData,
     fetchData,
   } = useListViewResource<Chart>('chart', t('chart'), addDangerToast);
-
   const {
     sliceCurrentlyEditing,
     openChartEditModal,
@@ -142,7 +141,7 @@ function ChartTable({
           },
         ]}
       />
-      {charts.length ? (
+      {charts?.length ? (
         <CardContainer>
           {charts.map(e => (
             <ChartCard


[incubator-superset] 33/33: lint fix

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 6d3e0121dc4df3ff71872e770cb0c530ebe79e96
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Mon Oct 26 17:24:46 2020 -0700

    lint fix
---
 .../spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx      | 2 --
 superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx          | 6 +++++-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx
index db1ec1f..eba5d66 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/ActivityTable_spec.tsx
@@ -24,12 +24,10 @@ import fetchMock from 'fetch-mock';
 import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
 import configureStore from 'redux-mock-store';
 import ActivityTable from 'src/views/CRUD/welcome/ActivityTable';
-import ListViewCard from 'src/components/ListViewCard';
 
 const mockStore = configureStore([thunk]);
 const store = mockStore({});
 
-const recentsEnpoint = 'glob:*/superset/recent_activity/*';
 const chartsEndpoint = 'glob:*/api/v1/chart/?*';
 const dashboardEndpoint = 'glob:*/api/v1/dashboard/?*';
 const savedQueryEndpoint = 'glob:*/api/v1/saved_query/?*';
diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
index c515911..dc80ddf 100644
--- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
@@ -147,7 +147,11 @@ export default function ActivityTable({ user }: ActivityProps) {
     getRecentAcitivtyObjs(user.userId, recent)
       .then(res => {
         const data: any = {
-          Created: [...res.createdByChart, ...res.createdByDash, ...res.createdByQuery],
+          Created: [
+            ...res.createdByChart,
+            ...res.createdByDash,
+            ...res.createdByQuery,
+          ],
           Edited: [...res.editedChart, ...res.editedDash],
         };
         if (res.viewed) {


[incubator-superset] 02/33: first steps

Posted by yj...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yjc pushed a commit to branch home-screen-mvp
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 624f23edfb78b540e00449a6303bf6319d68003b
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Fri Sep 18 07:38:10 2020 -0700

    first steps
---
 .../src/views/CRUD/welcome/DashboardTable.tsx      | 114 +++++++++++++++++++--
 .../src/views/CRUD/welcome/SavedQueries.tsx        |  60 +++++++++++
 .../src/views/CRUD/welcome/Welcome.tsx             |  29 +++++-
 3 files changed, 187 insertions(+), 16 deletions(-)

diff --git a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
index b3990f4..f0560e4 100644
--- a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
@@ -21,9 +21,18 @@ import { t, SupersetClient } from '@superset-ui/core';
 import { debounce } from 'lodash';
 import ListView, { FetchDataConfig } from 'src/components/ListView';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
+import ListViewCard from 'src/components/ListViewCard';
 import { Dashboard } from 'src/types/bootstrapTypes';
+import { Dropdown, Menu } from 'src/common/components';
+import Icon from 'src/components/Icon';
+import Label from 'src/components/Label';
+import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
 
-const PAGE_SIZE = 25;
+const PAGE_SIZE = 3;
+
+//const canEdit = hasPerm('can_edit');
+//const canDelete = hasPerm('can_delete');
+//const canExport = hasPerm('can_mulexport');
 
 interface DashboardTableProps {
   addDangerToast: (message: string) => void;
@@ -101,6 +110,16 @@ class DashboardTable extends React.PureComponent<
     };
   }
 
+  componentDidMount() {
+    console.log('component did mount!!!');
+    this.fetchDataDebounced({
+      pageSize: PAGE_SIZE,
+      pageIndex: 0,
+      sortBy: this.initialSort,
+      filters: [],
+    });
+  }
+
   componentDidUpdate(prevProps: DashboardTableProps) {
     if (prevProps.search !== this.props.search) {
       this.fetchDataDebounced({
@@ -132,19 +151,91 @@ class DashboardTable extends React.PureComponent<
           : [],
       );
 
+    /*const { dashboardFilter, user} = this.props;
+    const filters = [];
+
+    if (dashboardFilter === "Mine") {
+      filters.push[{
+        {
+          col: "owners.id", // API does not allow filter by owner id
+          opr: "eq",
+          value: user.id
+        }
+      }];
+    } else {
+      filter.push[{
+        {
+          col: "favorite", // API currently can't filter by favorite
+          opr: "eq",
+          value: true
+        }
+      }];
+    }*/
+
+    /*const menu = (
+      <Menu>
+        {canDelete && (
+          <Menu.Item>
+            <ConfirmStatusChange
+              title={t('Please Confirm')}
+              description={
+                <>
+                  {t('Are you sure you want to delete')}{' '}
+                </>
+              }
+              onConfirm={() => handleDashboardDelete(dashboard)}
+            >
+              {confirmDelete => (
+                <div
+                  role="button"
+                  tabIndex={0}
+                  className="action-button"
+                  onClick={confirmDelete}
+                >
+                  <ListViewCard.MenuIcon name="trash" /> Delete
+                </div>
+              )}
+            </ConfirmStatusChange>
+          </Menu.Item>
+        )}
+        {canExport && (
+          <Menu.Item
+            role="button"
+            tabIndex={0}
+            onClick={() => handleBulkDashboardExport([dashboard])}
+          >
+            <ListViewCard.MenuIcon name="share" /> Export
+          </Menu.Item>
+        )}
+        {canEdit && (
+          <Menu.Item
+            role="button"
+            tabIndex={0}
+            onClick={() => openDashboardEditModal(dashboard)}
+          >
+            <ListViewCard.MenuIcon name="pencil" /> Edit
+          </Menu.Item>
+        )}
+      </Menu>
+    ); */
+
+    console.log('sortBy', sortBy);
     const queryParams = JSON.stringify({
       order_column: sortBy[0].id,
       order_direction: sortBy[0].desc ? 'desc' : 'asc',
       page: pageIndex,
       page_size: pageSize,
+      //filters,
       ...(filterExps.length ? { filters: filterExps } : {}),
     });
-
+    console.log('hello!!!');
     return SupersetClient.get({
       endpoint: `/api/v1/dashboard/?q=${queryParams}`,
     })
       .then(({ json }) => {
+        console.log('json', json);
         this.setState({ dashboards: json.result, dashboard_count: json.count });
+        console.log('this.state', this.state);
       })
       .catch(response => {
         if (response.status === 401) {
@@ -168,15 +259,16 @@ class DashboardTable extends React.PureComponent<
 
   render() {
     return (
-      <ListView
-        columns={this.columns}
-        data={this.state.dashboards}
-        count={this.state.dashboard_count}
-        pageSize={PAGE_SIZE}
-        fetchData={this.fetchData}
-        loading={this.state.loading}
-        initialSort={this.initialSort}
-      />
+      <div>
+        {this.state.dashboards.map(e => (
+          <ListViewCard
+            title={e.dashboard_title}
+            loading={this.state.loading}
+            titleRight={<Label>{e.published ? 'published' : 'draft'}</Label>}
+            description={t('Last modified %s', e.changed_on_delta_humanized)}
+          />
+        ))}
+      </div>
     );
   }
 }
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
new file mode 100644
index 0000000..62e9e0e
--- /dev/null
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import { t, SupersetClient } from '@superset-ui/core';
+import { debounce } from 'lodash';
+import withToasts from 'src/messageToasts/enhancers/withToasts';
+import ListViewCard from 'src/components/ListViewCard';
+
+class SavedQueries extends React.PureComponent {
+  constructor(props: Readonly<{}>) {
+    super(props);
+    this.state = {
+      queries: [],
+    };
+  }
+  componentDidMount(){
+    this.fetchData();
+  }
+  fetchData = () => {
+    try {
+      const { json } = SupersetClient.get({
+        endpoint: `/api/v1/query/`,
+      });
+      this.setState({ queries: json.result });
+    } catch (e) {
+      return console.log(e);
+    }
+  };
+  render() {
+    return (
+      <div>
+        {this.state.queries.map(q => (
+          <ListViewCard
+            title={q.database_name}
+            loading={false}
+            description={t('Last run ', q.end_time)}
+          />
+        ))}
+      </div>
+    )
+  }
+}
+
+export default SavedQueries; 
\ No newline at end of file
diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index 15d4928..5e311dd 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -31,6 +31,7 @@ import { useQueryParam, StringParam, QueryParamConfig } from 'use-query-params';
 import { User } from 'src/types/bootstrapTypes';
 import RecentActivity from 'src/profile/components/RecentActivity';
 import Favorites from 'src/profile/components/Favorites';
+import SavedQueries from './SavedQueries';
 import DashboardTable from './DashboardTable';
 
 const { Panel } = Collapse;
@@ -68,7 +69,7 @@ export default function Welcome({ user }: WelcomeProps) {
     StringParam,
     'all',
   );
-
+  const [dashboardFilter, setDashboardFilter] = useState('Favorite');
   const [searchQuery, setSearchQuery] = useSyncQueryState(
     'search',
     StringParam,
@@ -91,8 +92,9 @@ export default function Welcome({ user }: WelcomeProps) {
     <Collapse defaultActiveKey={['1']}>
       <Panel header={t('Recents')} key="1">
         <SubMenu
-          activeChild="Datasets"
-          name={''}
+          activeChild="Viewed"
+          name=""
+          // eslint-disable-next-line react/no-children-prop
           children={[
             {
               name: 'Viewed',
@@ -115,6 +117,23 @@ export default function Welcome({ user }: WelcomeProps) {
       </Panel>
 
       <Panel header={t('Dashboards')} key="2">
+      <SubMenu
+          activeChild={dashboardFilter}
+          name=""
+          // eslint-disable-next-line react/no-children-prop
+          children={[
+            {
+              name: 'Favorite',
+              label: t('Favorite'),
+              onClick: () => setDashboardFilter('Favorite'),
+            },
+            {
+              name: 'Mine',
+              label: t('Mine'),
+              onClick: () => setDashboardFilter('Mine'),
+            },
+          ]}
+        />
         <FormControl
           type="text"
           bsSize="sm"
@@ -123,11 +142,11 @@ export default function Welcome({ user }: WelcomeProps) {
           // @ts-ignore React bootstrap types aren't quite right here
           onChange={e => setSearchQuery(e.currentTarget.value)}
         />
-        <DashboardTable search={searchQuery} />
+        <DashboardTable search={searchQuery} filter={dashboardFilter} />
       </Panel>
 
       <Panel header={t('Saved Queries')} key="3" >
-        Stuff here!
+        <SavedQueries />
       </Panel>
       <Panel header={t('Charts')} key="4" >
         Stuff here!