From 75fe7e7e00e657954922367ed04fa7abd9840473 Mon Sep 17 00:00:00 2001
From: riahk <moar.riah@gmail.com>
Date: Tue, 22 Sep 2020 11:42:37 -0700
Subject: [PATCH 01/13] saved query list view

---
 .../CRUD/data/savedquery/SavedQueryList.tsx   | 160 +++++++++++++++++-
 superset/queries/saved_queries/api.py         |   2 +
 2 files changed, 161 insertions(+), 1 deletion(-)

diff --git a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
index 4b55ee4c2e7f8..8f7075f4f6fd8 100644
--- a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
+++ b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
@@ -17,28 +17,186 @@
  * under the License.
  */
 
-import React from 'react';
+import { t } from '@superset-ui/core';
+import React, { useMemo } from 'react';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
+import { useListViewResource } from 'src/views/CRUD/hooks';
 import SubMenu, { SubMenuProps } from 'src/components/Menu/SubMenu';
+import ListView, { Filters } from 'src/components/ListView';
+import TooltipWrapper from 'src/components/TooltipWrapper';
+import Icon from 'src/components/Icon';
 import { commonMenuData } from 'src/views/CRUD/data/common';
 
+const PAGE_SIZE = 25;
+
 interface SavedQueryListProps {
   addDangerToast: (msg: string) => void;
   addSuccessToast: (msg: string) => void;
 }
 
+type SavedQueryObject = {};
+
 function SavedQueryList({
   addDangerToast,
   addSuccessToast,
 }: SavedQueryListProps) {
+  const {
+    state: { loading, resourceCount: queryCount, resourceCollection: queries },
+    hasPerm,
+    fetchData,
+    // refreshData, //TODO: add back later when editing?
+  } = useListViewResource<SavedQueryObject>(
+    'saved_query',
+    t('saved_queries'),
+    addDangerToast,
+  );
+
+  const canCreate = hasPerm('can_add');
+  const canEdit = hasPerm('can_edit');
+  const canDelete = hasPerm('can_delete');
+
   const menuData: SubMenuProps = {
     activeChild: 'Saved Queries',
     ...commonMenuData,
   };
 
+  const initialSort = [{ id: 'label', desc: true }];
+  const columns = useMemo(
+    () => [
+      {
+        accessor: 'label',
+        Header: t('Name'),
+      },
+      {
+        accessor: 'database',
+        Header: t('Database'),
+        Cell: ({
+          row: {
+            original: { database },
+          },
+        }: any) => `${database.database_name}`,
+      },
+      {
+        accessor: 'schema',
+        Header: t('Schema'),
+      },
+      {
+        Cell: ({
+          row: {
+            original: { sql_tables: tables },
+          },
+        }: any) => {
+          const names = tables.map((table: any) => table.table);
+
+          return names.join(', ');
+        },
+        accessor: 'sql_tables',
+        Header: t('Tables'),
+      },
+      {
+        Cell: ({
+          row: {
+            original: { changed_on_delta_humanized: changedOn },
+          },
+        }: any) => changedOn,
+        Header: t('Modified'),
+        accessor: 'changed_on_delta_humanized',
+      },
+      {
+        Cell: ({ row: { original } }: any) => {
+          const handleEdit = () => {}; // handleQueryEdit(original);
+          const handleDelete = () => {}; // openQueryDeleteModal(original);
+          if (!canEdit && !canDelete) {
+            return null;
+          }
+          return (
+            <span className="actions">
+              {canEdit && (
+                <TooltipWrapper
+                  label="edit-action"
+                  tooltip={t('Edit')}
+                  placement="bottom"
+                >
+                  <span
+                    role="button"
+                    tabIndex={0}
+                    className="action-button"
+                    onClick={handleEdit}
+                  >
+                    <Icon name="pencil" />
+                  </span>
+                </TooltipWrapper>
+              )}
+              {canDelete && (
+                <span
+                  role="button"
+                  tabIndex={0}
+                  className="action-button"
+                  data-test="database-delete"
+                  onClick={handleDelete}
+                >
+                  <TooltipWrapper
+                    label="delete-action"
+                    tooltip={t('Delete database')}
+                    placement="bottom"
+                  >
+                    <Icon name="trash" />
+                  </TooltipWrapper>
+                </span>
+              )}
+            </span>
+          );
+        },
+        Header: t('Actions'),
+        id: 'actions',
+        disableSortBy: true,
+      },
+    ],
+    [canDelete, canCreate],
+  );
+
+  const filters: Filters = useMemo(
+    () => [
+      {
+        Header: t('Database'),
+        id: 'database',
+        input: 'select',
+        operator: 'eq',
+        unfilteredLabel: 'All',
+        selects: [],
+      },
+      {
+        Header: t('Schema'),
+        id: 'schema',
+        input: 'select',
+        operator: 'eq',
+        unfilteredLabel: 'All',
+        selects: [],
+      },
+      {
+        Header: t('Search'),
+        id: 'label',
+        input: 'search',
+        operator: 'ct',
+      },
+    ],
+    [],
+  );
+
   return (
     <>
       <SubMenu {...menuData} />
+      <ListView<SavedQueryObject>
+        className="saved_query-list-view"
+        columns={columns}
+        count={queryCount}
+        data={queries}
+        fetchData={fetchData}
+        filters={filters}
+        initialSort={initialSort}
+        loading={loading}
+        pageSize={PAGE_SIZE}
+      />
     </>
   );
 }
diff --git a/superset/queries/saved_queries/api.py b/superset/queries/saved_queries/api.py
index af0dcd1c8d18a..d8f87fe8d2b61 100644
--- a/superset/queries/saved_queries/api.py
+++ b/superset/queries/saved_queries/api.py
@@ -73,6 +73,8 @@ class SavedQueryRestApi(BaseSupersetModelRestApi):
         "sql_tables",
     ]
     list_columns = [
+        "created_on",
+        "changed_on_delta_humanized",
         "created_by.first_name",
         "created_by.id",
         "created_by.last_name",

From c982164f685392cc561627a96bd7b4d723aeffa2 Mon Sep 17 00:00:00 2001
From: riahk <moar.riah@gmail.com>
Date: Tue, 22 Sep 2020 15:53:36 -0700
Subject: [PATCH 02/13] add tables popover

---
 .../CRUD/data/savedquery/SavedQueryList.tsx   | 51 ++++++++++++++++++-
 1 file changed, 49 insertions(+), 2 deletions(-)

diff --git a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
index 8f7075f4f6fd8..bca8ede116480 100644
--- a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
+++ b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
@@ -17,8 +17,9 @@
  * under the License.
  */
 
-import { t } from '@superset-ui/core';
+import { t, styled } from '@superset-ui/core';
 import React, { useMemo } from 'react';
+import { Popover } from 'src/common/components';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import { useListViewResource } from 'src/views/CRUD/hooks';
 import SubMenu, { SubMenuProps } from 'src/components/Menu/SubMenu';
@@ -36,6 +37,19 @@ interface SavedQueryListProps {
 
 type SavedQueryObject = {};
 
+const StyledTableLabel = styled.div`
+  .count {
+    margin-left: 5px;
+    color: ${({ theme }) => theme.colors.primary.base};
+    text-decoration: underline;
+    cursor: pointer;
+  }
+`;
+
+const StyledPopoverItem = styled.div`
+  color: ${({ theme }) => theme.colors.grayscale.dark2};
+`;
+
 function SavedQueryList({
   addDangerToast,
   addSuccessToast,
@@ -87,11 +101,44 @@ function SavedQueryList({
           },
         }: any) => {
           const names = tables.map((table: any) => table.table);
+          const main = names.shift();
+
+          if (names.length) {
+            return (
+              <StyledTableLabel>
+                <span>{main}</span>
+                <Popover
+                  placement="right"
+                  title={t('TABLES')}
+                  trigger="click"
+                  content={
+                    <>
+                      {names.map((name: string) => (
+                        <StyledPopoverItem>{name}</StyledPopoverItem>
+                      ))}
+                    </>
+                  }
+                >
+                  <span className="count">(+{names.length})</span>
+                </Popover>
+              </StyledTableLabel>
+            );
+          }
 
-          return names.join(', ');
+          return main;
         },
         accessor: 'sql_tables',
         Header: t('Tables'),
+        disableSortBy: true,
+      },
+      {
+        Cell: ({
+          row: {
+            original: { created_on: createdOn },
+          },
+        }: any) => createdOn,
+        Header: t('Created On'),
+        accessor: 'created_on',
       },
       {
         Cell: ({

From cf066ef93ac0f667f1421a62b224b1d03cd2dd0e Mon Sep 17 00:00:00 2001
From: riahk <moar.riah@gmail.com>
Date: Wed, 23 Sep 2020 10:31:08 -0700
Subject: [PATCH 03/13] update actions

---
 superset-frontend/images/icons/clipboard.svg  | 21 ++++++++++
 .../CRUD/data/savedquery/SavedQueryList.tsx   | 40 ++++++++++++++++---
 2 files changed, 55 insertions(+), 6 deletions(-)
 create mode 100644 superset-frontend/images/icons/clipboard.svg

diff --git a/superset-frontend/images/icons/clipboard.svg b/superset-frontend/images/icons/clipboard.svg
new file mode 100644
index 0000000000000..850ca4c146a93
--- /dev/null
+++ b/superset-frontend/images/icons/clipboard.svg
@@ -0,0 +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.
+-->
+<svg width="18" height="21" viewBox="0 0 18 21" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M18 7.44C17.9896 7.34813 17.9695 7.25763 17.94 7.17V7.08C17.8919 6.97718 17.8278 6.88267 17.75 6.8L11.75 0.8C11.6673 0.722216 11.5728 0.658081 11.47 0.61C11.4402 0.60576 11.4099 0.60576 11.38 0.61C11.2784 0.551741 11.1662 0.514344 11.05 0.5H7C5.34315 0.5 4 1.84315 4 3.5V4.5H3C1.34315 4.5 0 5.84315 0 7.5V17.5C0 19.1569 1.34315 20.5 3 20.5H11C12.6569 20.5 14 19.1569 14 17.5V16.5H15C16.6569 16.5 18 15.1569 18 13.5V7.5C18 7.5 18 7.5 18 7.44ZM12 3.91L14.59 6.5H13C12.4477 6.5 12 6.05228 12 5.5V3.91ZM12 17.5C12 18.0523 11.5523 18.5 11 18.5H3C2.44772 18.5 2 18.0523 2 17.5V7.5C2 6.94772 2.44772 6.5 3 6.5H4V13.5C4 15.1569 5.34315 16.5 7 16.5H12V17.5ZM16 13.5C16 14.0523 15.5523 14.5 15 14.5H7C6.44772 14.5 6 14.0523 6 13.5V3.5C6 2.94772 6.44772 2.5 7 2.5H10V5.5C10 7.15685 11.3431 8.5 13 8.5H16V13.5Z" fill="currentColor"/>
+</svg>
diff --git a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
index bca8ede116480..17281c687e4f3 100644
--- a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
+++ b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
@@ -151,17 +151,31 @@ function SavedQueryList({
       },
       {
         Cell: ({ row: { original } }: any) => {
-          const handleEdit = () => {}; // handleQueryEdit(original);
+          const handlePreview = () => {}; // openQueryPreviewModal(original); // TODO: open preview modal
+          const handleEdit = () => {}; // handleQueryEdit(original); // TODO: navigate to sql editor with selected query open
+          const handleCopy = () => {}; // TODO: copy link to clipboard
           const handleDelete = () => {}; // openQueryDeleteModal(original);
-          if (!canEdit && !canDelete) {
-            return null;
-          }
+
           return (
             <span className="actions">
+              <TooltipWrapper
+                label="preview-action"
+                tooltip={t('Query preview')}
+                placement="bottom"
+              >
+                <span
+                  role="button"
+                  tabIndex={0}
+                  className="action-button"
+                  onClick={handlePreview}
+                >
+                  <Icon name="binoculars" />
+                </span>
+              </TooltipWrapper>
               {canEdit && (
                 <TooltipWrapper
                   label="edit-action"
-                  tooltip={t('Edit')}
+                  tooltip={t('Edit query')}
                   placement="bottom"
                 >
                   <span
@@ -174,6 +188,20 @@ function SavedQueryList({
                   </span>
                 </TooltipWrapper>
               )}
+              <TooltipWrapper
+                label="copy-action"
+                tooltip={t('Copy query URL')}
+                placement="bottom"
+              >
+                <span
+                  role="button"
+                  tabIndex={0}
+                  className="action-button"
+                  onClick={handleCopy}
+                >
+                  <Icon name="clipboard" />
+                </span>
+              </TooltipWrapper>
               {canDelete && (
                 <span
                   role="button"
@@ -184,7 +212,7 @@ function SavedQueryList({
                 >
                   <TooltipWrapper
                     label="delete-action"
-                    tooltip={t('Delete database')}
+                    tooltip={t('Delete query')}
                     placement="bottom"
                   >
                     <Icon name="trash" />

From 217a6250af3b1413ae57fb1a5d431c127af4b8f9 Mon Sep 17 00:00:00 2001
From: riahk <moar.riah@gmail.com>
Date: Wed, 23 Sep 2020 14:04:21 -0700
Subject: [PATCH 04/13] finish sort, filters

---
 .../CRUD/data/savedquery/SavedQueryList.tsx   | 40 ++++++++++++++-----
 superset/queries/saved_queries/api.py         |  2 +
 2 files changed, 33 insertions(+), 9 deletions(-)

diff --git a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
index 17281c687e4f3..40a7c2675d5d0 100644
--- a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
+++ b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
@@ -19,6 +19,11 @@
 
 import { t, styled } from '@superset-ui/core';
 import React, { useMemo } from 'react';
+import {
+  createFetchRelated,
+  createFetchDistinct,
+  createErrorHandler,
+} from 'src/views/CRUD/utils';
 import { Popover } from 'src/common/components';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import { useListViewResource } from 'src/views/CRUD/hooks';
@@ -82,13 +87,13 @@ function SavedQueryList({
         Header: t('Name'),
       },
       {
-        accessor: 'database',
+        accessor: 'database.database_name',
         Header: t('Database'),
-        Cell: ({
-          row: {
-            original: { database },
-          },
-        }: any) => `${database.database_name}`,
+      },
+      {
+        accessor: 'database',
+        hidden: true,
+        disableSortBy: true,
       },
       {
         accessor: 'schema',
@@ -236,9 +241,19 @@ function SavedQueryList({
         Header: t('Database'),
         id: 'database',
         input: 'select',
-        operator: 'eq',
+        operator: 'rel_o_m',
         unfilteredLabel: 'All',
-        selects: [],
+        fetchSelects: createFetchRelated(
+          'saved_query',
+          'database',
+          createErrorHandler(errMsg =>
+            t(
+              'An error occurred while fetching dataset datasource values: %s',
+              errMsg,
+            ),
+          ),
+        ),
+        paginate: true,
       },
       {
         Header: t('Schema'),
@@ -246,7 +261,14 @@ function SavedQueryList({
         input: 'select',
         operator: 'eq',
         unfilteredLabel: 'All',
-        selects: [],
+        fetchSelects: createFetchDistinct(
+          'saved_query',
+          'schema',
+          createErrorHandler(errMsg =>
+            t('An error occurred while fetching schema values: %s', errMsg),
+          ),
+        ),
+        paginate: true,
       },
       {
         Header: t('Search'),
diff --git a/superset/queries/saved_queries/api.py b/superset/queries/saved_queries/api.py
index d8f87fe8d2b61..522980868021c 100644
--- a/superset/queries/saved_queries/api.py
+++ b/superset/queries/saved_queries/api.py
@@ -96,6 +96,8 @@ class SavedQueryRestApi(BaseSupersetModelRestApi):
         "sql",
         "created_by.first_name",
         "database.database_name",
+        "created_on",
+        "changed_on_delta_humanized",
     ]
 
     search_filters = {"label": [SavedQueryAllTextFilter]}

From 04684fac51e8da528a8f08c48c899f35893d2ad4 Mon Sep 17 00:00:00 2001
From: riahk <moar.riah@gmail.com>
Date: Thu, 24 Sep 2020 14:07:18 -0700
Subject: [PATCH 05/13] add fetchMock to spec

---
 .../data/savedquery/SavedQueryList_spec.jsx   | 57 +++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx b/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx
index cba919e6c59b1..8e2d916a41ec8 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx
@@ -19,15 +19,68 @@
 import React from 'react';
 import thunk from 'redux-thunk';
 import configureStore from 'redux-mock-store';
+import fetchMock from 'fetch-mock';
 import { styledMount as mount } from 'spec/helpers/theming';
 import SavedQueryList from 'src/views/CRUD/data/savedquery/SavedQueryList';
 import SubMenu from 'src/components/Menu/SubMenu';
+import ListView from 'src/components/ListView';
+// import Filters from 'src/components/ListView/Filters';
 import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
+// import { act } from 'react-dom/test-utils';
 
 // store needed for withToasts(DatabaseList)
 const mockStore = configureStore([thunk]);
 const store = mockStore({});
 
+const queriesInfoEndpoint = 'glob:*/api/v1/saved_query/_info*';
+const queriesEndpoint = 'glob:*/api/v1/saved_query/?*';
+const queriesRelatedEndpoint = 'glob:*/api/v1/saved_query/related/database?*';
+const queriesDistinctEndpoint = 'glob:*/api/v1/saved_query/distinct/schema?*';
+
+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}`,
+  label: `query ${i}`,
+  schema: 'public',
+  sql: `SELECT ${i} FROM table`,
+  sql_tables: [
+    {
+      catalog: null,
+      schema: null,
+      table: `${i}`,
+    },
+  ],
+}));
+
+fetchMock.get(queriesInfoEndpoint, {
+  permissions: ['can_delete'],
+});
+fetchMock.get(queriesEndpoint, {
+  result: mockqueries,
+  count: 3,
+});
+
+fetchMock.get(queriesRelatedEndpoint, {
+  count: 0,
+  result: [],
+});
+
+fetchMock.get(queriesDistinctEndpoint, {
+  count: 0,
+  result: [],
+});
+
 describe('SavedQueryList', () => {
   const wrapper = mount(<SavedQueryList />, { context: { store } });
 
@@ -42,4 +95,8 @@ describe('SavedQueryList', () => {
   it('renders a SubMenu', () => {
     expect(wrapper.find(SubMenu)).toExist();
   });
+
+  it('renders a ListView', () => {
+    expect(wrapper.find(ListView)).toExist();
+  });
 });

From 5c96a95d484f6563c8e20db0995c9c2351f49c6e Mon Sep 17 00:00:00 2001
From: riahk <moar.riah@gmail.com>
Date: Thu, 24 Sep 2020 14:34:59 -0700
Subject: [PATCH 06/13] add more specs

---
 .../data/savedquery/SavedQueryList_spec.jsx   | 24 +++++++++++++++++--
 .../CRUD/data/savedquery/SavedQueryList.tsx   |  2 +-
 2 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx b/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx
index 8e2d916a41ec8..871048f6e9a4c 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx
@@ -24,9 +24,9 @@ import { styledMount as mount } from 'spec/helpers/theming';
 import SavedQueryList from 'src/views/CRUD/data/savedquery/SavedQueryList';
 import SubMenu from 'src/components/Menu/SubMenu';
 import ListView from 'src/components/ListView';
-// import Filters from 'src/components/ListView/Filters';
+import Filters from 'src/components/ListView/Filters';
 import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
-// import { act } from 'react-dom/test-utils';
+import { act } from 'react-dom/test-utils';
 
 // store needed for withToasts(DatabaseList)
 const mockStore = configureStore([thunk]);
@@ -99,4 +99,24 @@ describe('SavedQueryList', () => {
   it('renders a ListView', () => {
     expect(wrapper.find(ListView)).toExist();
   });
+
+  it('fetches saved queries', () => {
+    const callsQ = fetchMock.calls(/saved_query\/\?q/);
+    expect(callsQ).toHaveLength(1);
+    expect(callsQ[0][0]).toMatchInlineSnapshot(
+      `"http://localhost/api/v1/saved_query/?q=(order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:25)"`,
+    );
+  });
+
+  it('searches', async () => {
+    const filtersWrapper = wrapper.find(Filters);
+    act(() => {
+      filtersWrapper.find('[name="label"]').first().props().onSubmit('fooo');
+    });
+    await waitForComponentToPaint(wrapper);
+
+    expect(fetchMock.lastCall()[0]).toMatchInlineSnapshot(
+      `"http://localhost/api/v1/saved_query/?q=(filters:!((col:label,opr:ct,value:fooo)),order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:25)"`,
+    );
+  });
 });
diff --git a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
index 40a7c2675d5d0..b8d892b133195 100644
--- a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
+++ b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
@@ -79,7 +79,7 @@ function SavedQueryList({
     ...commonMenuData,
   };
 
-  const initialSort = [{ id: 'label', desc: true }];
+  const initialSort = [{ id: 'changed_on_delta_humanized', desc: true }];
   const columns = useMemo(
     () => [
       {

From a267a85e510a8a0d3d48ba8a3ff619b1e26c7c32 Mon Sep 17 00:00:00 2001
From: riahk <moar.riah@gmail.com>
Date: Fri, 25 Sep 2020 12:57:44 -0700
Subject: [PATCH 07/13] rebase + change to all_text

---
 .../src/views/CRUD/data/savedquery/SavedQueryList.tsx           | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
index b8d892b133195..226c24068b228 100644
--- a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
+++ b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
@@ -274,7 +274,7 @@ function SavedQueryList({
         Header: t('Search'),
         id: 'label',
         input: 'search',
-        operator: 'ct',
+        operator: 'all_text',
       },
     ],
     [],

From f578561296ab18050e93289cc07f8cb963cbdff9 Mon Sep 17 00:00:00 2001
From: riahk <moar.riah@gmail.com>
Date: Fri, 25 Sep 2020 13:07:32 -0700
Subject: [PATCH 08/13] fix tests

---
 .../views/CRUD/data/savedquery/SavedQueryList_spec.jsx          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx b/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx
index 871048f6e9a4c..6778d8d92c52e 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx
@@ -116,7 +116,7 @@ describe('SavedQueryList', () => {
     await waitForComponentToPaint(wrapper);
 
     expect(fetchMock.lastCall()[0]).toMatchInlineSnapshot(
-      `"http://localhost/api/v1/saved_query/?q=(filters:!((col:label,opr:ct,value:fooo)),order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:25)"`,
+      `"http://localhost/api/v1/saved_query/?q=(filters:!((col:label,opr:all_text,value:fooo)),order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:25)"`,
     );
   });
 });

From 2cba4e353f515aa1f2bc06e0cc6309bba185b5c4 Mon Sep 17 00:00:00 2001
From: riahk <moar.riah@gmail.com>
Date: Fri, 25 Sep 2020 13:28:52 -0700
Subject: [PATCH 09/13] format created_on

---
 .../CRUD/data/savedquery/SavedQueryList.tsx    | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
index 226c24068b228..849b4faed3867 100644
--- a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
+++ b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
@@ -19,6 +19,7 @@
 
 import { t, styled } from '@superset-ui/core';
 import React, { useMemo } from 'react';
+import moment from 'moment';
 import {
   createFetchRelated,
   createFetchDistinct,
@@ -141,7 +142,22 @@ function SavedQueryList({
           row: {
             original: { created_on: createdOn },
           },
-        }: any) => createdOn,
+        }: any) => {
+          const date = new Date(createdOn);
+          const utc = new Date(
+            Date.UTC(
+              date.getFullYear(),
+              date.getMonth(),
+              date.getDate(),
+              date.getHours(),
+              date.getMinutes(),
+              date.getSeconds(),
+              date.getMilliseconds(),
+            ),
+          );
+
+          return moment(utc).fromNow();
+        },
         Header: t('Created On'),
         accessor: 'created_on',
       },

From 97aebf375dd58922ba353e42fdd6a96ccab101ab Mon Sep 17 00:00:00 2001
From: riahk <moar.riah@gmail.com>
Date: Mon, 28 Sep 2020 11:40:08 -0700
Subject: [PATCH 10/13] update icons

---
 .../src/views/CRUD/data/savedquery/SavedQueryList.tsx         | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
index 849b4faed3867..8832ed853d9f8 100644
--- a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
+++ b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
@@ -205,7 +205,7 @@ function SavedQueryList({
                     className="action-button"
                     onClick={handleEdit}
                   >
-                    <Icon name="pencil" />
+                    <Icon name="edit" />
                   </span>
                 </TooltipWrapper>
               )}
@@ -220,7 +220,7 @@ function SavedQueryList({
                   className="action-button"
                   onClick={handleCopy}
                 >
-                  <Icon name="clipboard" />
+                  <Icon name="copy" />
                 </span>
               </TooltipWrapper>
               {canDelete && (

From 9075f8d7cf1378dec857f6f92de164dbc12ea8ea Mon Sep 17 00:00:00 2001
From: riahk <moar.riah@gmail.com>
Date: Mon, 28 Sep 2020 14:52:35 -0700
Subject: [PATCH 11/13] rm extra copy icon

---
 superset-frontend/images/icons/clipboard.svg | 21 --------------------
 1 file changed, 21 deletions(-)
 delete mode 100644 superset-frontend/images/icons/clipboard.svg

diff --git a/superset-frontend/images/icons/clipboard.svg b/superset-frontend/images/icons/clipboard.svg
deleted file mode 100644
index 850ca4c146a93..0000000000000
--- a/superset-frontend/images/icons/clipboard.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
-  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.
--->
-<svg width="18" height="21" viewBox="0 0 18 21" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M18 7.44C17.9896 7.34813 17.9695 7.25763 17.94 7.17V7.08C17.8919 6.97718 17.8278 6.88267 17.75 6.8L11.75 0.8C11.6673 0.722216 11.5728 0.658081 11.47 0.61C11.4402 0.60576 11.4099 0.60576 11.38 0.61C11.2784 0.551741 11.1662 0.514344 11.05 0.5H7C5.34315 0.5 4 1.84315 4 3.5V4.5H3C1.34315 4.5 0 5.84315 0 7.5V17.5C0 19.1569 1.34315 20.5 3 20.5H11C12.6569 20.5 14 19.1569 14 17.5V16.5H15C16.6569 16.5 18 15.1569 18 13.5V7.5C18 7.5 18 7.5 18 7.44ZM12 3.91L14.59 6.5H13C12.4477 6.5 12 6.05228 12 5.5V3.91ZM12 17.5C12 18.0523 11.5523 18.5 11 18.5H3C2.44772 18.5 2 18.0523 2 17.5V7.5C2 6.94772 2.44772 6.5 3 6.5H4V13.5C4 15.1569 5.34315 16.5 7 16.5H12V17.5ZM16 13.5C16 14.0523 15.5523 14.5 15 14.5H7C6.44772 14.5 6 14.0523 6 13.5V3.5C6 2.94772 6.44772 2.5 7 2.5H10V5.5C10 7.15685 11.3431 8.5 13 8.5H16V13.5Z" fill="currentColor"/>
-</svg>

From 80e36acfb5f92fe95738be0f32452d659babc476 Mon Sep 17 00:00:00 2001
From: riahk <moar.riah@gmail.com>
Date: Mon, 28 Sep 2020 16:11:03 -0700
Subject: [PATCH 12/13] update api tests

---
 superset/queries/saved_queries/api.py    | 2 +-
 tests/queries/saved_queries/api_tests.py | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/superset/queries/saved_queries/api.py b/superset/queries/saved_queries/api.py
index 522980868021c..e8c72e28936d5 100644
--- a/superset/queries/saved_queries/api.py
+++ b/superset/queries/saved_queries/api.py
@@ -73,8 +73,8 @@ class SavedQueryRestApi(BaseSupersetModelRestApi):
         "sql_tables",
     ]
     list_columns = [
-        "created_on",
         "changed_on_delta_humanized",
+        "created_on",
         "created_by.first_name",
         "created_by.id",
         "created_by.last_name",
diff --git a/tests/queries/saved_queries/api_tests.py b/tests/queries/saved_queries/api_tests.py
index f268b1dc06231..b748d03352b84 100644
--- a/tests/queries/saved_queries/api_tests.py
+++ b/tests/queries/saved_queries/api_tests.py
@@ -116,6 +116,8 @@ def test_get_list_saved_query(self):
         data = json.loads(rv.data.decode("utf-8"))
         assert data["count"] == len(saved_queries)
         expected_columns = [
+            "changed_on_delta_humanized",
+            "created_on",
             "created_by",
             "database",
             "db_id",

From e46b64c48102e93dca2839768bfe58bc07c8e47e Mon Sep 17 00:00:00 2001
From: riahk <moar.riah@gmail.com>
Date: Tue, 29 Sep 2020 13:12:40 -0700
Subject: [PATCH 13/13] disable cypress test breaking CI

---
 .../cypress/integration/dashboard/edit_mode.test.js             | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.test.js b/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.test.js
index 47fbabcb94a14..ab4d8db7bf949 100644
--- a/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.test.js
+++ b/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.test.js
@@ -26,7 +26,7 @@ describe('Dashboard edit mode', () => {
     cy.get('.dashboard-header [data-test=edit-alt]').click();
   });
 
-  it('remove, and add chart flow', () => {
+  xit('remove, and add chart flow', () => {
     // wait for box plot to appear
     cy.get('.grid-container .box_plot');